LinuxQuestions.org
Welcome to the most active Linux Forum on the web.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 09-23-2004, 05:32 PM   #1
lappen
Member
 
Registered: Aug 2003
Location: Sweden
Posts: 83

Rep: Reputation: 15
Simple for loop


I am trying to make a simple for loop to be used in my firewall script, and being a complete noob in bash scripting I can't seem to figure out how to solve this.

Code:
IPT='/sbin/iptables'
MSN=1
ICQ=2
IRC=3

TCPALLOW="$MSN $ICQ"
UDPALLOW="$IRC"
for i in "$TCPALLOW" "$UDPALLOW";
do
echo $IPT -A INPUT -p tcp --dport $i -j ACCEPT
echo $IPT -A INPUT -p udp --dport $i -j ACCEPT
done
Is giving me
/sbin/iptables -A INPUT -p tcp --dport 1 2 -j ACCEPT
/sbin/iptables -A INPUT -p udp --dport 1 2 -j ACCEPT
/sbin/iptables -A INPUT -p tcp --dport 3 -j ACCEPT
/sbin/iptables -A INPUT -p udp --dport 3 -j ACCEPT

I would like it like this
/sbin/iptables -A INPUT -p tcp --dport 1 -j ACCEPT
/sbin/iptables -A INPUT -p tcp --dport 2 -j ACCEPT
/sbin/iptables -A INPUT -p udp --dport 3 -j ACCEPT


I would also like to expand it so if I give the VAR a variable like this
MSN=1/tcp
ICQ=2/udp
ALLOW="$MSN $ICQ"
for i in $ALLOW";
do
echo $IPT -A INPUT -p FROMTHEVAR --dport $i -j ACCEPT
done


Any help to get me going appreciated
 
Old 09-23-2004, 07:32 PM   #2
Hko
Senior Member
 
Registered: Aug 2002
Location: Groningen, The Netherlands
Distribution: Debian
Posts: 2,536

Rep: Reputation: 111Reputation: 111
Re: Simple for loop

You need to remove all 4 double quotes from the for-statement:
Code:
for i in $TCPALLOW $UDPALLOW;
Quote:
would also like to expand it so if I give the VAR a variable like this
Code:
MSN=1/tcp
ICQ=2/udp
ALLOW="$MSN $ICQ"
for i in $ALLOW";
do
echo $IPT -A INPUT -p FROMTHEVAR --dport $i -j ACCEPT
done
Code:
#!/bin/bash

IPT='/sbin/iptables'
MSN=1/tcp
ICQ=2/tcp
IRC=3/udp

ALLOW="$MSN $ICQ $IRC"

for i in $ALLOW ; do
    echo $IPT -A INPUT -p ${i#*/} --dport ${i%/*} -j ACCEPT
done
(to read how ${i#*/} and ${i%/*} work, search for ## in the bash man page.)
 
Old 09-24-2004, 06:54 PM   #3
lappen
Member
 
Registered: Aug 2003
Location: Sweden
Posts: 83

Original Poster
Rep: Reputation: 15
thanks, some complicate stuff there.

Anyways I am trying to expand on your example and I don't really understand howto implement it.

Code:
#!/bin/bash

# Locations.
IPT='/sbin/iptables'

# Interfaces
INT_IF='eth0'
EXT_IF='eth1'
INT_IP=`ifconfig $INT_IF     | grep -w inet | cut -d : -f 2 | cut -d ' ' -f 1`
EXT_IP=`ifconfig $EXT_IF     | grep -w inet | cut -d : -f 2 | cut -d ' ' -f 1`
I want to have it as flexible as possible so I wont need to have several loops
udp doesnt support syn so some sort of if(isset(var['udp'])) { $syn = null; }
Code:
# Allow
SSH=('22/tcp/INPUT/ACCEPT')
TEST=('3000/udp/INPUT/ACCEPT')
HTTP=('INPUT/80/tcp/ACCEPT' 'FORWARD/80/tcp/ACCEPT/$INT_IF/$EXT_IF')
# $INT -> $EXT should add $IPT -A ${i%@/} -p ${i#/*} -i $INT_IF -o $EXT_IF ......

ALLOW=('$SSH' '$HTTP')
for i in ${HTTP[@]}
do
echo $IPT -A ${i%@/} -p ${i#/*} --syn -m state --state NEW -j ${i#*/}
done
I know the {i%@/} is completely wrong, but cant seem to understand how to implement it from the man page...
Doesn't matter what I put in there it is printing the whole array instead of splitting it..
cant seem to figure out how to use $ALLOW instead of $VAR either

Last edited by lappen; 09-24-2004 at 06:56 PM.
 
Old 09-25-2004, 08:20 AM   #4
Hko
Senior Member
 
Registered: Aug 2002
Location: Groningen, The Netherlands
Distribution: Debian
Posts: 2,536

Rep: Reputation: 111Reputation: 111
Quote:
lappen wrote
Code:
SSH=('22/tcp/INPUT/ACCEPT')
TEST=('3000/udp/INPUT/ACCEPT')
HTTP=('INPUT/80/tcp/ACCEPT' 'FORWARD/80/tcp/ACCEPT/$INT_IF/$EXT_IF')
First fo all, you need to have the fields in these vars in the same order to be able to process them in a loop.
I mean: for SSH you have "port/protocol/chain/destiny", but for HTTP you have them in a different order: "chain/port/protocol/destiny". This way the script won't be able to decide which field means what iptables parameter.
  • In your case arrays are not needed, and it's easier without them.
  • Use double quotes in SSH, HTTP... var's, so it's possible to use other vars inside these strings (e.g. $INT_IF)
  • The slash separator (/) is a bit unfortunate when using "set" as I did below. So I changed it to commas.
  • Adding "--syn" when protocol is tcp, as you asked, is not needed because you're using connection tracking, so "--state NEW" will already take care of it. It is done anyway in the script below.
Please try if this is what you are looking for:
Code:
#!/bin/bash

IPT='/sbin/iptables'

INT_IF='eth0'
EXT_IF='eth1'

# RULES:
# ~~~~~~
# Format: 
# <chain>,tcp|udp,<port>,ACCEPT|DROP,[<internal interface>],[<external interface>]
#
SSH="INPUT,tcp,22,ACCEPT"
TEST="INPUT,udp,3000,ACCEPT"
HTTP="INPUT,tcp,80,ACCEPT   FORWARD,80,tcp,ACCEPT,$INT_IF,$EXT_IF"

ALLOW="$SSH $TEST $HTTP"

OLDIFS=$IFS  # Save old field seperator
for i in $ALLOW ; do
	IFS=','  # Set field seperator for $1, $2, $3, ... to comma.
	set $i   # Sets the fields of the rules into $1, $2, $3, ...
	
	INT="${5:+-i $5}"  # Sets $INT to "-i $5" *only* when $5 is set to something.
	EXT="${6:+-o $6}"  # Sets $EXT to "-o $6" *only* when $6 is set to something.

	# While not needed, adding "--syn" if protocol is tcp:
	#
	test "$2" == "tcp" && SYN="--syn" || unset SYN
	
	echo $IPT -A $1 -p $2 --dport $3 $INT $EXT $SYN -m state --state NEW -j $4
done
IFS=$OLDIFS  # Set field seperator back to original one.

Last edited by Hko; 09-25-2004 at 08:22 AM.
 
Old 09-25-2004, 11:02 AM   #5
lappen
Member
 
Registered: Aug 2003
Location: Sweden
Posts: 83

Original Poster
Rep: Reputation: 15
Quote:
Originally posted by Hko
Please try if this is what you are looking for:
This is exactly what I was trying to accomplish, except for one thing which I thought about right now and it doesn't seem like it would be possible to do that without arrays();

Sometimes I would need to 'DESTINY' both tcp, udp or sometimes tcp,udp and icmp

-p, --protocol [!] protocol
The protocol of the rule or of the packet to check. The specified protocol can be one of
tcp, udp, icmp, or all, or it can be a numeric value, representing one of these protocols or
a different one. A protocol name from /etc/protocols is also allowed. A "!" argument
before the protocol inverts the test. The number zero is equivalent to all. Protocol all
will match with all protocols and is taken as default when this option is omitted.

$IPT -p all or 0 'should' take care of it but it doesn't work for me?

So if possible would it be difficult to add
TEST="INPUT,tcp udp,3000,ACCEPT"
TEST2="INPUT,tcp udp icmp,3001,ACCEPT"

Instead of having
TCPTEST=" "
UDPTEST=" "
ICMPTEST=" "

ALLOW="$TCPEST $UDPTEST $ICMPTEST"

Thanks again...
 
Old 09-25-2004, 12:48 PM   #6
Hko
Senior Member
 
Registered: Aug 2002
Location: Groningen, The Netherlands
Distribution: Debian
Posts: 2,536

Rep: Reputation: 111Reputation: 111
Quote:
Originally posted by lappen
This is exactly what I was trying to accomplish, except for one thing which I thought about right now and it doesn't seem like it would be possible to do that without arrays();
Still no arrays needed,.. Maybe when you extend your script more.
Quote:
So if possible would it be difficult to add
TEST="INPUT,tcp udp,3000,ACCEPT"
TEST2="INPUT,tcp udp icmp,3001,ACCEPT"
That won't be possible, because iptables itsef does not allow multiple protocols be specified. The man page you quoted already says this (when you read carefully). Specifying two -p options in the same iptables command is also invalid (e.g: iptbales -A INPUT -p tcp -p icmp is invalid)
Quote:
-p, --protocol [!] protocol
The protocol of the rule or of the packet to check. The specified protocol can be one of tcp, udp, icmp, or all, or it can be a numeric value, representing one of these protocols or a different one.
So, not for the protocol, but when you want to use the multiport feature, comma-seperated list may be needed:
iptables -A INPUT -m multiport --dports 80,443,113,21

..so, its probably better (with the future of your script in mind) to change the seperator once again. To colon ( : ) this time.

When you want to specify "-p udp,icmp" (invalid), you can use "-p ! tcp" instead. This isn't supported by the script I posted above, because the ! needs a space after it, which interferes with the way the script reads the rules.

This case is fixed below by testing for the !, and then add an extra space. Other, more general, solutions are thinkable, but require more changes to the script. e.g. changing the rule-seperator from space to somthing else, but for reading them in the loop other means of seperating them would be needed.

In the script below, both the seperator and the ! case are fixed:
Code:
#!/bin/bash

IPT='/sbin/iptables'

INT_IF='eth0'
EXT_IF='eth1'

# RULES:
# ~~~~~~
# Format: 
# <chain>:<protocol-spec>:<port>:ACCEPT|DROP:[<internal interface>]:[<external interface>]
#
SSH="INPUT:tcp:22:ACCEPT"
TEST="INPUT:!udp:3000:ACCEPT" # Note there's no space after '!' here. Will be added later.
HTTP="INPUT:tcp:80:ACCEPT   FORWARD:tcp:80:ACCEPT:$INT_IF:$EXT_IF"

ALLOW="$SSH $TEST $HTTP"

OLDIFS=$IFS
for i in $ALLOW ; do
	IFS=':'
	set $i
	INT="${5:+-i $5}"
	EXT="${6:+-o $6}"

	# If protocol starts with '!', add space after it:
	PROTO="$2"
	test "${PROTO:0:1}" == "!" && PROTO="! ${PROTO:1}"
	
	echo $IPT -A $1 -p "$PROTO" --dport $3 $INT $EXT -m state --state NEW -j $4
done
IFS=$OLDIFS
P.S. Recommended reading: http://www.tldp.org/LDP/abs/html/index.html

Last edited by Hko; 09-25-2004 at 01:01 PM.
 
Old 09-25-2004, 01:47 PM   #7
lappen
Member
 
Registered: Aug 2003
Location: Sweden
Posts: 83

Original Poster
Rep: Reputation: 15
I wasn't thinking about posting multiple -p --protocols in one rule, more something like this


Code:
SSH="INPUT:ARRAY('tcp' 'udp'):22:ACCEPT"
ALLOW="$SSH"

your_code....

        # not knowing bash programming 'yet' I will try to post this with incorrect examples
        # I hope you understand what I mean
        if(is_array($PROTO)) {
          for ($PROTO = 0; $i = count($PROTO[]) {
            echo $IPT -A $1 -p $PROTO['$i'] --dport $3 $INT $EXT -m state --state NEW -j $4
            done
          }
        } else {
        # run the regular statement
           echo $IPT -A $1 -p "$PROTO" --dport $3 $INT $EXT -m state --state NEW -j $4
           done
        }
If $PROTO is an ARRAY, count that ARRAY. SSH would be equals to two, then run the $IPT -A $1 -p $PROTO['$i'] --dport $3 $INT $EXT -m state --state NEW -j $4 rule twice so I would get
$IPT -A $1 -p tcp --dport $3 $INT $EXT -m state --state NEW -j $4
$IPT -A $1 -p udp --dport $3 $INT $EXT -m state --state NEW -j $4

then check others in $ALLOW, if they have several ports defined in an array do the same else run the default rule..


I will read that howto you posted, seems a bit easier to understand then the man page, thanks again
 
Old 09-25-2004, 05:30 PM   #8
Hko
Senior Member
 
Registered: Aug 2002
Location: Groningen, The Netherlands
Distribution: Debian
Posts: 2,536

Rep: Reputation: 111Reputation: 111
Quote:
Code:
SSH="INPUT:ARRAY('tcp' 'udp'):22:ACCEPT"
ALLOW="$SSH"

# [..snip..]
        if(is_array($PROTO)) {
          for ($PROTO = 0; $i = count($PROTO[]) {
            echo $IPT -A $1 -p $PROTO['$i'] --dport $3 $INT $EXT -m state --state NEW -j $4
            done
          }
        }
Sorry to say, but IMHO this is overdoing it. I suppose, the vars like "SSH="INPUT:tcp..." are there to make easy to change/add flter rules. But now their own syntax is getting (a little) complicated. And so is the script code.

Also, it's not needed: All possible protocol combinations are covered by the syntax iptables already offers (remeber there're only 3 of them):
  • tcp = tcp
  • upd = udp
  • icmp = icmp
  • tcp,udp = ! icmp
  • tcp,icmp = ! udp
  • udp,icmp = ! tcp
Quote:
I will read that howto you posted, seems a bit easier to understand then the man page,
It certainly is more clear tahn the man page. The man page is a reference with very compact information, and the howto is a guide for learning bash scripting.
 
Old 09-25-2004, 06:01 PM   #9
lappen
Member
 
Registered: Aug 2003
Location: Sweden
Posts: 83

Original Poster
Rep: Reputation: 15
Quote:
Also, it's not needed: All possible protocol combinations are covered by the syntax iptables already offers (remeber there're only 3 of them):
I didn't think when reading the man page, never thought twice about what the ! option did.. but now its clear to me that arrays arent neccesary

EDIT:
Why isn't --syn needed?

I have checked the man page and found no reference about it and when doing a iptables -L without the --syn bit it is generating
tcp dpt:ssh state NEW
while using the syn bit I get
tcp dpt:ircd flags:SYN,RST,ACK/SYN state NEW


Some problems with the FORWARD rule when specifying interfaces
Warning: wierd character in interface `-i eth0' (No aliases, :, ! or *).
Warning: wierd character in interface `-o eth1' (No aliases, :, ! or *).

seems it is trying to store the complete value off
${parameter:+-i}

this is the value it is storing it in the ruleset right now 'iptables -v -L'
-i eth0 -o eth1
it should be
eth0 eth1

thanks again

Last edited by lappen; 09-25-2004 at 07:45 PM.
 
Old 09-26-2004, 06:02 AM   #10
Hko
Senior Member
 
Registered: Aug 2002
Location: Groningen, The Netherlands
Distribution: Debian
Posts: 2,536

Rep: Reputation: 111Reputation: 111
Quote:
EDIT:
Why isn't --syn needed?

I have checked the man page and found no reference about it and when doing a iptables -L without the --syn bit it is generating
tcp dpt:ssh state NEW
while using the syn bit I get
tcp dpt:ircd flags:SYN,RST,ACK/SYN state NEW
I start having doubts about what I said about -syn...
Maybe someone else with more iptables knowledge can eloborate on that?

Here's why I think --syn is obsolete in an iptables rule with "--state NEW":
The TCP SYN flag is only set on the very first packet, the one that opens a TCP-connection. As long as the connection is alive (after the syn-packet, until closed), there will be no packtet in the communication with the SYN flag set.

The syn flag is the (only?) indicator for a fresh new tcp-connection coming in. So, "--state NEW" must check for the syn flag, to determine the connection is new.

Just the fact that listing the rules with "iptables -L" still lists "SYN" doesn't convince me that I am wrong here: The runle syntax with --syn still is correct, and iptables loaded the rule into the kernel just like we said.

Why I start doubting:
There can be other reasons for specifying --syn while using connection tracking (-m state). Like: checking for illegal tcp-sequences during the tcp connections, or e.g. to still have some new-connection-detection when the state-module happens not to be loaded for some reason. Another case can be to check that SYN is not set on a packet in an ongoing connection when having filtering rules for connections started from the local network.

Also, I don't have full overview on this. I'm not an iptables expert, though I know my way around in it in not-too-complex packet filters. So I may have overlooked some conrener case(s).

About the illegal character in "-i eth0 -o eth1":
I didn't notice because I was using "echo $IPT" al the time..
To fix, the command "IFS=$OLDIFS" should be moved so it's inside the loop, before the actual iptables command.
Code:
#!/bin/bash

IPT='/sbin/iptables'

INT_IF='eth0'
EXT_IF='eth1'

# RULES:
# ~~~~~~
# Format: 
# <chain>:<protocol-spec>:<port>:ACCEPT|DROP:[<internal interface>]:[<external interface>]
#
SSH="INPUT:tcp:22:ACCEPT"
TEST="INPUT:!udp:3000:ACCEPT"
HTTP="INPUT:tcp:80:ACCEPT   FORWARD:tcp:80:ACCEPT:$INT_IF:$EXT_IF"

ALLOW="$SSH $TEST $HTTP"

OLDIFS=$IFS
for i in $ALLOW ; do
	IFS=':'
	set $i
	INT="${5:+-i $5}"
	EXT="${6:+-o $6}"

	# If protocol starts with '!', add an extra space:
	PROTO="$2"
	test "${PROTO:0:1}" == "!" && PROTO="! ${PROTO:1}"

	IFS=$OLDIFS
	$IPT -A $1 -p  $PROTO --dport $3 $INT $EXT $SYN -m state --state NEW -j $4
done
A similar problem still exists when using "not" ( ! ) with the protocol. I don't have a solution for that now.
 
Old 09-26-2004, 06:36 AM   #11
lappen
Member
 
Registered: Aug 2003
Location: Sweden
Posts: 83

Original Poster
Rep: Reputation: 15
A bit much for me to grasp considering I am kinda new to iptables, will go without the --syn bit and if I learn otherwise I will add it later

And Thanks, works like a charm...

Last edited by lappen; 09-26-2004 at 06:38 AM.
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
Problem with simple shell script for loop abefroman Programming 2 10-25-2005 08:26 PM
problems creating a simple bootable cd to perform a simple task czarherr Linux - Software 1 11-11-2004 05:22 AM
[c shell] simple for loop saiz66 Programming 1 09-28-2004 07:02 PM
Simple while loop problem (newbie question) Seventh Programming 3 09-07-2004 12:00 PM
awk - simple 'for' loop doing my nut... davee Programming 2 06-30-2004 08:54 AM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 05:06 AM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration