3

GNU bash, version 4.3.42(1)-release (arm-openwrt-linux-gnu)

I'm trying to script an iptables rule using a bash array.

$NETID is used to create network addresses. For example, 192.168.10.0/24, 192.168.20.0/24, etc.

I'm trying to create a temporary variable ($NETID_TMP) where the "i" in ${NETID[@]} is removed from $NETID_TMP. For example, if "i" = 10, NETID_TMP=(20 30 40).

Script:

    #!/opt/bin/bash
    NETID=(10 20 30 40)
    for (( i=0; i<${#NETID[@]}; i++ )); do
       NETID_TMP=(${NETID[*]})
       unset NETID_TMP[${NETID[i]}]
       iptables -I FORWARD -s 192.168.${NETID[i]}.0/24 -d 192.168.${NETID_TMP[0]}.0/24,192.168.${NETID_TMP[1]}.0/24,192.168.${NETID_TMP[2]}.0/24 -j DROP
    done

The result should be ...

iptables -I FORWARD -s 192.168.10.0/24 -d 192.168.20.0/24,192.168.30.0/24,192.168.40.0/24 -j DROP
iptables -I FORWARD -s 192.168.20.0/24 -d 192.168.10.0/24,192.168.30.0/24,192.168.40.0/24 -j DROP
iptables -I FORWARD -s 192.168.30.0/24 -d 192.168.10.0/24,192.168.20.0/24,192.168.40.0/24 -j DROP
iptables -I FORWARD -s 192.168.40.0/24 -d 192.168.10.0/24,192.168.20.0/24,192.168.30.0/24 -j DROP
4
  • 1
    Which element do you want to remove from the array? Your question title and content doesn't seem to go well with each other? Could you please explain or rephrase?
    – Bussller
    Commented Sep 2, 2017 at 6:16
  • K. Which element from the NETID array that you want to remove? All I can see is, elements are present in different IPs that you've mentioned.
    – Bussller
    Commented Sep 2, 2017 at 6:27
  • Now I see, you want to iteratively remove elements and inject them in appropriate place? Am I correct?
    – Bussller
    Commented Sep 2, 2017 at 6:33
  • Yes. $NETID will always equal (10 20 30 40). $NETID_TMP is based on $NETID, but has the "i" element removed (i.e., "10"). When the for loop iterates, the "i" element will move to the second element (20) and $NETID_TMP would consist of $NETID minus the second element (i.e., "10 30 40".
    – uihdff
    Commented Sep 2, 2017 at 6:40

2 Answers 2

4
netid=(10 20 30 40)

for i in "${netid[@]}"; do
    out="iptables -I FORWARD -s 192.168.$i.0/24 -d "

    for j in "${netid[@]}"; do
        (( i == j )) && continue
        out+="192.168.$j.0/24,"
    done
    out="${out%,} -j DROP"

    printf '%s\n' "$out"
done

This Bash script produces

iptables -I FORWARD -s 192.168.10.0/24 -d 192.168.20.0/24,192.168.30.0/24,192.168.40.0/24 -j DROP
iptables -I FORWARD -s 192.168.20.0/24 -d 192.168.10.0/24,192.168.30.0/24,192.168.40.0/24 -j DROP
iptables -I FORWARD -s 192.168.30.0/24 -d 192.168.10.0/24,192.168.20.0/24,192.168.40.0/24 -j DROP
iptables -I FORWARD -s 192.168.40.0/24 -d 192.168.10.0/24,192.168.20.0/24,192.168.30.0/24 -j DROP

through a double loop. The inner loop skips the iterations where the loop variable is numerically equal to the loop variable of the outer loop to create the desired effect.

Once you have the string in $out, you may eval it.

Alternatively, to avoid eval:

netid=(10 20 30 40)

for i in "${netid[@]}"; do
    sarg="192.168.$i.0/24"
    darg=""

    for j in "${netid[@]}"; do
        (( i == j )) && continue
        darg+="192.168.$j.0/24,"
    done
    darg="${darg%,}"

    iptables -I FORWARD -s "$sarg" -d "$darg" -j DROP
done

The variable substitution ${parameter%word} will remove word from the very end of $parameter, so darg="${darg%,}" will remove the comma at the end af $darg.


Update after question in comment:

The only thing bash-specific about the above is the use of the array $netid, the += operator to append to the $darg string and ((...)). We may turn this into a sh script like this (here assuming $IFS still has its default value):

netid="10 20 30 40"

for i in $netid; do
    sarg="192.168.$i.0/24"
    darg=""

    for j in $netid; do
        [ "$i" -eq "$j" ] && continue
        darg="${darg}192.168.$j.0/24,"
    done
    darg="${darg%,}"

    iptables -I FORWARD -s "$sarg" -d "$darg" -j DROP
done

If you have a separate list of numbers that you'd like to plug in for $darg, use that in the inner loop in place of $netid. It should be a string of space-separated numbers.

9
  • excellent!! ^_^. You're so fast, when I got the concept, you produced the result. Great :D
    – Bussller
    Commented Sep 2, 2017 at 7:07
  • @kusalananda - Does "(( i == j )) && continue" mean if "i" = "j" to skip the next command and return to the "for j in ..." line ?
    – uihdff
    Commented Sep 2, 2017 at 10:45
  • 1
    @uihdff Yes, exactly. It corresponds to next in Perl. It continues with the next iteration of the current loop.
    – Kusalananda
    Commented Sep 2, 2017 at 10:47
  • 1
    @uihdff See updated answer.
    – Kusalananda
    Commented Sep 4, 2017 at 14:24
  • 1
    @uihdff Yes, and if any number in $DST_ID is equal to the current number $i, then that number will be skipped.
    – Kusalananda
    Commented Sep 4, 2017 at 14:40
2

It's easier with zsh:

#! /bin/zsh -
nets=(192.168.{10,20,30,40}.0/24)
for net ($nets) iptables -I FORWARD -s $net -d ${(j:,:)nets:#$net} -j DROP

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .