iptables-save output using dotted quad netmask rather than CIDR

iptables-save on rhel5 outputs: -A RH-Firewall-1-INPUT -s 12.3.4.5/255.255.0.0 -p tcp -m tcp -j ACCEPT whereas rhel6 outputs -A RH-Firewall-1-INPUT -s 12.3.4.5/16 -p tcp -m tcp -j ACCEPT Wanting to normalise iptables-save to one form or the other (preferably using the dotted quad netmask), the best I can come up with is a line by line grep (for optimisation perhaps, since a match wont happen often) for /<number> and then extract the number, pass to cidr2mask, and replace /<number> in that line (this is part of a self contained shell script that will be executed on the fly on another host, so I'd rather not rely on anything that isn't already in RHEL, ie no writing a perl sript myself unless it's a one liner perl -e thing). What flag to iptables-save am I missing where it does this for me? -- Tim Connors

On Tue, 18 Jun 2013, Tim Connors wrote:
iptables-save on rhel5 outputs:
-A RH-Firewall-1-INPUT -s 12.3.4.5/255.255.0.0 -p tcp -m tcp -j ACCEPT
whereas rhel6 outputs
-A RH-Firewall-1-INPUT -s 12.3.4.5/16 -p tcp -m tcp -j ACCEPT
Wanting to normalise iptables-save to one form or the other (preferably using the dotted quad netmask), the best I can come up with is a line by line grep (for optimisation perhaps, since a match wont happen often) for /<number> and then extract the number, pass to cidr2mask, and replace /<number> in that line (this is part of a self contained shell script that will be executed on the fly on another host, so I'd rather not rely on anything that isn't already in RHEL, ie no writing a perl sript myself unless it's a one liner perl -e thing).
What flag to iptables-save am I missing where it does this for me?
This seems to be a suitably hacking way of doing it: # build an array of all replacements /0../32 to /0.0.0.0 -> # /255.255.255.255 so that any occurences can quickly be replaced # globally in any required filter cidr2netmaskfilter= for cidr in `seq 0 32` ; do netmask=`cidr2mask "$cidr"` cidr2netmaskfilter="$cidr2netmaskfilter; s!/$cidr !/$netmask!g" done function filter_cidr2mask () { sed "$cidr2netmaskfilter" } And in combination with filter_remove_comment() I can now do silly things like: function filter_remove_comment () { sed 's/ -m comment --comment "[^"]*"//' } if iptables-save | filter_cidr2mask | filter_remove_comment | grep "^-A $chain $ruleremove" > /dev/null ; then verboserun iptables -D $chain $ruleremove modified=true fi Weee! (yes, I'm trying to do something like puppetize our iptables configurations without using puppet, and without using the various puppet iptables patterns which I found very deficient). -- Tim Connors

On 2013-06-18 19:04, Tim Connors wrote: [...]
This seems to be a suitably hacking way of doing it:
# build an array of all replacements /0../32 to /0.0.0.0 -> # /255.255.255.255 so that any occurences can quickly be replaced # globally in any required filter cidr2netmaskfilter= for cidr in `seq 0 32` ; do netmask=`cidr2mask "$cidr"` cidr2netmaskfilter="$cidr2netmaskfilter; s!/$cidr !/$netmask!g" done
function filter_cidr2mask () { sed "$cidr2netmaskfilter" } [...]
Hi Tim, A couple of pointers: I'd discourage the use of seq[1]. If you're using bash, which I assume you are given that's available on all RHEL instances, I'd use {0..32} instead of `seq 0 32`. I also advice using $(cidr2mask "$cidr") rather than `cidr2mask "$cidr"`; The $() notation is newer and more predictable, especially with regard to quoting. Also, what *is* cidr2mask? I've not seen it anywhere before. Further, your sed appears to be expecting a space after "$cidr", but isn't putting one back after "$netmask". That could give you grief. Finally, were I writing this myself (in the absense of cidr2mask), I'd probably do something like the following: | filter_cidr2mask(){ | cidr=( | 0.0.0.0 | 128.0.0.0 | 192.0.0.0 | 224.0.0.0 | 240.0.0.0 | 248.0.0.0 | 252.0.0.0 | 254.0.0.0 | 255.0.0.0 | 255.128.0.0 | 255.192.0.0 | 255.224.0.0 | 255.240.0.0 | 255.248.0.0 | 255.252.0.0 | 255.254.0.0 | 255.255.0.0 | 255.255.128.0 | 255.255.192.0 | 255.255.224.0 | 255.255.240.0 | 255.255.248.0 | 255.255.252.0 | 255.255.254.0 | 255.255.255.0 | 255.255.255.128 | 255.255.255.192 | 255.255.255.224 | 255.255.255.240 | 255.255.255.248 | 255.255.255.252 | 255.255.255.254 | 255.255.255.255 | ) | | sedExp= | for ((i=0; i<=32; i++)); do | sedExp+="s!/\<$i\>!/${cidr[$i]}!g; " | done | | sed "$sedExp" | } 1. <greybot> seq(1) is a highly nonstandard program used to count to 10 in silly Linux howtos. Use one of these: for ((i = 1; i <= 10; i++)); do ... (bash 2.04+/zsh/ksh93), i=; while ((i++ <= 10)); do ... (bash/mksh/ksh88), i=; while [ $(( ( i += 1 ) <= 10 )) -ne 0 ]; do ... (POSIX). Don't do this: for x in $(...); do ... Don't do this: for i in {1..10}; do ... -- Regards, Matthew Cengia

On Tue, 18 Jun 2013, Matthew Cengia wrote:
On 2013-06-18 19:04, Tim Connors wrote: [...]
This seems to be a suitably hacking way of doing it:
# build an array of all replacements /0../32 to /0.0.0.0 -> # /255.255.255.255 so that any occurences can quickly be replaced # globally in any required filter cidr2netmaskfilter= for cidr in `seq 0 32` ; do netmask=`cidr2mask "$cidr"` cidr2netmaskfilter="$cidr2netmaskfilter; s!/$cidr !/$netmask!g" done
function filter_cidr2mask () { sed "$cidr2netmaskfilter" } [...]
Hi Tim,
A couple of pointers: I'd discourage the use of seq[1]. If you're using bash, which I assume you are given that's available on all RHEL instances, I'd use {0..32} instead of `seq 0 32`.
Never heard of that before. Definitely not POSIX (gut feeling here - no POSIX reference manual handy, but it doesn't work in dash). Never seen seq not work (assumed it was posix - I even saw it work on HP-UX of all things. Heck, it probably even works on SCO).
I also advice using $(cidr2mask "$cidr") rather than `cidr2mask "$cidr"`; The $() notation is newer and more predictable, especially with regard to quoting.
Yeah, trying to get out of the habit (` is still prettier for small snippets), however I thought $(prog) is not POSIX (I'm straying well away from posix in this program anyway), whereas ` is. (how do you google for "`" and "$(" ?)
Also, what *is* cidr2mask? I've not seen it anywhere before.
You own favourite shell function. Not the one I originally wrote which seemed to work for the common cases until I threw a non-class address at it while testing months after initial deploy (woops!) (meh, famous last words, it's not like it's ever going to be used elsewhere other than our class B). Here's some useful network calculations someone might find useful (after suitably debugging and commenting), that should never be deployed in production because I do silly things like work out what $0 is (so can be symlinked to netcalc, bcasecacl, cidr2mask, mask2cidr) without any sanity checking: http://rather.puzzling.org/~tconnors/code/cidr2mask
Further, your sed appears to be expecting a space after "$cidr", but isn't putting one back after "$netmask". That could give you grief.
Woops, that what happens when you want to fly out of the door before 7 and it's already 7:04. Tested fine! Good thing I ran with --noop rather than deploying across the cluster :) It's only me on call. Guess I better check my phone to see whether anyone called while I was on the bike :)
Finally, were I writing this myself (in the absense of cidr2mask), I'd probably do something like the following:
| filter_cidr2mask(){ | cidr=( | 0.0.0.0 | 128.0.0.0 | 192.0.0.0 | 224.0.0.0 | 240.0.0.0 | 248.0.0.0 | 252.0.0.0 | 254.0.0.0 | 255.0.0.0 | 255.128.0.0 | 255.192.0.0 | 255.224.0.0 | 255.240.0.0 | 255.248.0.0 | 255.252.0.0 | 255.254.0.0 | 255.255.0.0 | 255.255.128.0 | 255.255.192.0 | 255.255.224.0 | 255.255.240.0 | 255.255.248.0 | 255.255.252.0 | 255.255.254.0 | 255.255.255.0 | 255.255.255.128 | 255.255.255.192 | 255.255.255.224 | 255.255.255.240 | 255.255.255.248 | 255.255.255.252 | 255.255.255.254 | 255.255.255.255 | )
Urk. Big array not generated by a simple loop.
| sedExp= | for ((i=0; i<=32; i++)); do | sedExp+="s!/\<$i\>!/${cidr[$i]}!g; " | done | | sed "$sedExp" | }
1. <greybot> seq(1) is a highly nonstandard program used to count to 10 in silly Linux howtos. Use one of these: for ((i = 1; i <= 10; i++)); do ... (bash 2.04+/zsh/ksh93), i=; while ((i++ <= 10)); do ... (bash/mksh/ksh88), i=; while [ $(( ( i += 1 ) <= 10 )) -ne 0 ]; do ... (POSIX). Don't do this: for x in $(...); do ... Don't do this: for i in {1..10}; do ...
Heh. -- Tim Connors

On 2013-06-18 22:08, Tim Connors wrote: [...]
instances, I'd use {0..32} instead of `seq 0 32`.
Never heard of that before. Definitely not POSIX (gut feeling here - no POSIX reference manual handy, but it doesn't work in dash). Never seen seq not work (assumed it was posix - I even saw it work on HP-UX of all things. Heck, it probably even works on SCO).
You're right, {0..32} isn't POSIX; I did further research further down the email, most notably the stuff from Greybot (which, for reference, lurks in Freenode/#bash).
I also advice using
Oops. *advise
$(cidr2mask "$cidr") rather than `cidr2mask "$cidr"`; The $() notation is newer and more predictable, especially with regard to quoting.
Yeah, trying to get out of the habit (` is still prettier for small snippets), however I thought $(prog) is not POSIX (I'm straying well away from posix in this program anyway), whereas ` is. (how do you google for "`" and "$(" ?)
Both POSIX (at least for newer POSIX versions). For reference: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag... (Also worth noting though that, e.g. Solaris sh doesn't adhere to current POSIX, and implements `` but not $().) [...]
Here's some useful network calculations someone might find useful (after suitably debugging and commenting), that should never be deployed in production because I do silly things like work out what $0 is (so can be symlinked to netcalc, bcasecacl, cidr2mask, mask2cidr) without any sanity checking:
Pfft, that's easily fixed: case "${0##*/}" in mask2cidr|cidr2mask|netcalc|bcastcalc) "${0##*/}";; *) echo 'ERROR!' >&2; exit 1;; esac ... And that's all POSIX, not that it matters given you're invoking as /bin/bash. [...]
Urk. Big array not generated by a simple loop.
I agree, but I couldn't come up with a more elegant solution in the time I allocated myself. Will take a look at your script for ideas. -- Regards, Matthew Cengia

Tim Connors writes:
-A RH-Firewall-1-INPUT -s 12.3.4.5/16 -p tcp -m tcp -j ACCEPT
One gotcha, which applies at -restore time, but not at -save time: Like IPv6, in IPv4 you can omit .0 segments: 1.4 --> 1.0.0.4 1.2.4 --> 1.2.0.4 (I think - might be 1.0.2.4) iptables-restore understands this. However, if there is a CIDR it expands differently: 1.4/24 --> 1.4.0.0/24 1.2.4/24 --> 1.2.4.0/24

On Wed, 19 Jun 2013, Trent W. Buck wrote:
Tim Connors writes:
-A RH-Firewall-1-INPUT -s 12.3.4.5/16 -p tcp -m tcp -j ACCEPT
One gotcha, which applies at -restore time, but not at -save time:
Like IPv6, in IPv4 you can omit .0 segments:
1.4 --> 1.0.0.4 1.2.4 --> 1.2.0.4 (I think - might be 1.0.2.4)
I missed that went it was sent originally.
iptables-restore understands this. However, if there is a CIDR it expands differently:
1.4/24 --> 1.4.0.0/24 1.2.4/24 --> 1.2.4.0/24
Holy crap that's ridiculous. Anyone who uses those stupid formats gets what they deserve. -- Tim Connors

Tim Connors wrote:
On Wed, 19 Jun 2013, Trent W. Buck wrote:
Tim Connors writes:
-A RH-Firewall-1-INPUT -s 12.3.4.5/16 -p tcp -m tcp -j ACCEPT
One gotcha, which applies at -restore time, but not at -save time:
Like IPv6, in IPv4 you can omit .0 segments:
1.4 --> 1.0.0.4 1.2.4 --> 1.2.0.4 (I think - might be 1.0.2.4)
I missed that went it was sent originally.
iptables-restore understands this. However, if there is a CIDR it expands differently:
1.4/24 --> 1.4.0.0/24 1.2.4/24 --> 1.2.4.0/24
Holy crap that's ridiculous. Anyone who uses those stupid formats gets what they deserve.
No worries -- nobody can afford a whole class B anymore anyway ;-P BTW 1.4 --> 1.0.0.4 works in ping and suchlike, too. Not nmap, tho.
participants (4)
-
Matthew Cengia
-
Tim Connors
-
Trent W. Buck
-
trentbuck@gmail.com