Como iterar através de uma variável enquanto pula um dos valores?

3

Usando o kernel 2.6.x

Observação: Em uma pergunta anterior, tentei fazer isso com uma matriz bash. Neste sistema operacional, shells não-sh são instalados como pacotes Entware-NG e não podem ser usados, pois são carregados após a execução do script.

Meta: Rotule a criação de regras de iptables usando sh (não bash, zsh, etc.) com o seguinte formato. Para cada interface em $ NAME ...

  • Crie uma regra para o endereço IP da rede de origem na posição correspondente em $ NETID. O primeiro valor em $ NAME tem um endereço de rede de origem criado a partir do primeiro valor em $ NETID. Por exemplo, -i eth1 corresponde a -src 192.168.10.0/24 e -i eth2 corresponde a 192.168.20.0/24.
  • Crie o endereço IP de destino para os valores em $ NETID não usados no endereço de rede de origem. Por exemplo, -src 192.168.10.0/24 -dst 192.168.20.0/24.
  • Construa cada regra com um endereço de rede de destino. Não especifique vários endereços para o valor -dst.

Resultado desejado:

iptables -I FORWARD -i eth1 -src 192.168.10.0/24 -dst 192.168.20.0/24
iptables -I FORWARD -i eth1 -src 192.168.10.0/24 -dst 192.168.30.0/24
iptables -I FORWARD -i eth2 -src 192.168.20.0/24 -dst 192.168.10.0/24
iptables -I FORWARD -i eth2 -src 192.168.20.0/24 -dst 192.168.30.0/24
iptables -I FORWARD -i eth3 -src 192.168.30.0/24 -dst 192.168.10.0/24
iptables -I FORWARD -i eth3 -src 192.168.30.0/24 -dst 192.168.20.0/24

Problema: O script abaixo não cria uma relação 1: 1 com o nome da interface e o valor -src. Por exemplo, eth1 e 192.168. 10 .0 / 24, eth2 e 192.168. 20 .0 / 24 , etc.

#!/bin/sh
NETID="10 20 30"
NAME="eth1 eth2 eth3"



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

   for j in $NETID; do
      [ "$i" -eq "$j" ] && continue
      darg="192.168.$j.0/24"
      for k in $NAME; do
         echo "iptables -I FORWARD -i $k -s $sarg -d $darg -j DROP"
      done
   done
done
    
por uihdff 08.09.2017 / 03:20

3 respostas

1

O principal problema é que você tem um relacionamento 1: 1 entre os nomes de interface (eth1, eth2, ...) e as redes associadas (10.x, 20.x, ...), mas não há nada em seu código que estabelece explicitamente esse relacionamento de 1: 1.

Você deve em algum lugar vincular ethN a 192.168.N.x . Você pode fazer isso com uma única variável NETS , que conteria uma lista de interface:N pares. Veja meu código abaixo:

#!/bin/sh
NETS="eth1:10 eth2:20 eth3:30"

for net in $NETS; do
    k="${net%:*}"          # Yields the name of the interface
    i="${net#*:}"          # Yields the number bound to the interface
    sarg="192.168.$i.0/24"

    for net in $NETS; do
        j="${net#*:}"      # Yields the number bound to the interface
        [ "$i" -eq "$j" ] && continue
        darg="192.168.$j.0/24"
        echo "iptables -I FORWARD -i $k -s $sarg -d $darg -j DROP"
    done
done

Notas:

  • Eu removi completamente o ciclo for k in $NAME , pois parece contradizer o que você deseja alcançar.
  • Você pode até colocar a rede na definição de NETS e ler sarg e darg diretamente dela. Algo como:

    NETS="eth1:192.168.10.0/24 eth2:192.168.20.0/24 eth3:192.168.30.0/24"
    

[update] Combinado com minha resposta à sua outra pergunta , isso daria esse script:

#!/bin/sh
NETID="10 20 30"
NAME="eth1 eth2 eth3"
NETS=$(set -- $NETID; for iface in $NAME; do echo "$iface:192.168.$1.0/24"; shift; done)

for net in $NETS; do
    iface="${net%:*}"
    sarg="${net#*:}"

    for net in $NETS; do
        darg="${net#*:}"
        [ "$sarg" != "$darg" ] && echo "iptables -I FORWARD -i $k -s $sarg -d $darg -j DROP"
    done
done
    
por 10.09.2017 / 13:43
3

Usando o POSIX Awk e não se preocupando com a sequência das linhas de saída:

echo | awk -v 'netids=10 20 30' -v 'names=eth1 eth2 eth3' '
  BEGIN {
    netidcount = split(netids, netid)
    namecount = split(names, name)
    if (netidcount != namecount) {
      print "Error: You must pass the same number of names and netids."
      exit 1
    }
    for (i in name) {
      for (j in name) {
        if (i == j) {
          continue
        }
        printf "iptables -I FORWARD -i %s -src 192.168.%s.0/24 -dst 192.168.%s.0/24 -j DROP\n", name[i], netid[i], netid[j]
      }
    }
  }'

Claro que você pode variar isso; por exemplo, você pode colocar o script awk acima em um arquivo e chamá-lo com:

echo | awk -v ... -v ... -f script.awk

Ou, você pode colocar os netid e os nomes em data.txt , talvez formatados da seguinte forma:

eth1 10
eth2 20
eth3 30

E escreva o arquivo de script Awk script.awk da seguinte forma:

#!/bin/awk
NF != 2 {
  error = 1
  print "Error: Each line of input must contain one interface name and one number."
  exit
}
(($1 in nameseen) || ($2 in netidseen)) {
  error = 1
  print "Error: Each interface name and network id in input must be unique."
  exit
}
{
  name[NR] = $1
  netid[NR] = $2
  nameseen[$1]
  netidseen[$2]
}
END {
  if (error) {
    exit error
  }
  for (i in name) {
    for (j in name) {
      if (i == j) {
        continue
      }
      printf "iptables -I FORWARD -i %s -src 192.168.%s.0/24 -dst 192.168.%s.0/24 -j DROP\n", name[i], netid[i], netid[j]
    }
  }
}

E, em seguida, ligue assim:

awk -f script.awk data.txt

Muitas opções.

Eu provavelmente teria data.txt parecido com:

eth1 192.168.10.0/24
eth2 192.168.20.0/24
eth3 192.168.30.0/24

A modificação do script para trabalhar com esse formato de dados é deixada como um exercício para o leitor. ;)

(Observação: se você usar esse código, lembre-se de incluir um link para essa resposta como atribuição).

    
por 08.09.2017 / 18:09
2

versão POSIX sh:

for n in 1 2 3; do
  src="$n"0
  interface="eth$n"
  for dest in 10 20 30; do
    [ "$dest" = "$src" ] && continue
    printf 'iptables -I FORWARD -i %s -src %s -dst %s -j DROP\n' "$interface" "192.168.$src.0/24" "192.168.$dest.0/24"
  done
done

Eu fiz algumas suposições sobre como você está vinculando a interface à fonte para suas regras. Mas isso produz a saída desejada que você mostrou.

    
por 08.09.2017 / 04:11