1

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
  • 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 Sep 2 '17 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 Sep 2 '17 at 6:27
  • Now I see, you want to iteratively remove elements and inject them in appropriate place? Am I correct? – Bussller Sep 2 '17 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 Sep 2 '17 at 6:40
3
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.

| improve this answer | |
  • excellent!! ^_^. You're so fast, when I got the concept, you produced the result. Great :D – Bussller Sep 2 '17 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 Sep 2 '17 at 10:45
  • 1
    @uihdff Yes, exactly. It corresponds to next in Perl. It continues with the next iteration of the current loop. – Kusalananda Sep 2 '17 at 10:47
  • 1
    @uihdff See updated answer. – Kusalananda Sep 4 '17 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 Sep 4 '17 at 14:40
1

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
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.