kohler / click

The Click modular router: fast modular packet processing and analysis
Other
734 stars 324 forks source link

EtherSwitch configuration #479

Open p4pe opened 3 years ago

p4pe commented 3 years ago

Hello, I'm using EtherSwitch element in order to implement a simple switch (at first) with only one input and one output port. The click configuration is this:

from_port0::FromDevice(enp6s0f1)
to_port0::ToDevice(enp6s0f0)
in_supp, out_supp :: Suppressor;
switch::EtherSwitch()
q0 :: Queue -> to_port0;

from_port0->[0] in_supp [0] -> [0] switch [0] -> [0] out_supp [0] ->q0;

But when I'm trying to start the router, I can't and took this error:

switch.click:4: ‘switch :: EtherSwitch’ input 1 unused switch.click:4: ‘switch :: EtherSwitch’ output 1 unused

Where is my misconfiguration?

ahenning commented 3 years ago

your config says that a packet received on port 0 is sent out port 0. A switch prevents this.

from_eth0 -> [0]switch[1] -> to_eth1; from_eth1 -> [1]switch[0] -> to_eth0;

p4pe commented 3 years ago

Yes I know that a switch prevents this. It was a misunderstanding from me and I thought that the output port 0 is not the same port with the input port 0. I believe that were different ports.

tbarbette commented 3 years ago

You have to build at least 2 ports to your switch, or it can't "switch" :p You have this kind of requirements in the documentation at https://github.com/kohler/click/wiki/EtherSwitch.

from_port0::FromDevice(enp6s0f0)
from_port1::FromDevice(enp6s0f1)
to_port0::ToDevice(enp6s0f0)
to_port1::ToDevice(enp6s0f1)
in_supp0, out_supp0 :: Suppressor;
in_supp1, out_supp1 :: Suppressor;
switch::EtherSwitch()
q0 :: Queue -> to_port0;
q1 :: Queue -> to_port1;

from_port0->in_supp0  -> [0] switch [0] ->  out_supp0  ->q0;
from_port1->in_supp1  -> [1] switch [1] ->  out_supp1  ->q1;

Note that I have from and to port0 tied to enp6sf0, else I think it's a call for problem.

p4pe commented 3 years ago

Yes, it was my misunderstanding, Ι don't know what I was thinking when I read the documentation. :p :P

p4pe commented 3 years ago

@tbarbette I saw again your configuration, If I am not completely wrong, we have ingress port enp6s0f0 and also egress port enp6s0f0.

Is this right? or I did not understand the element?

In my scenario I want something like this: switch

I followed @ahenning example. So I create a tap interface on the Switch for the incoming traffic from the vnf

 Idle -> tap :: KernelTap(1.1.0.1/24, DEVNAME vEth1);
switch::EtherSwitch()
***The right dashed line***
FromDevice(enp6s0f2)-> [0]switch[1]->Queue->ToDevice(enp6s0f3)
FromDevice(enp6s0f3)-> [1]switch[0] -> Queue -> ToDevice(enp6s0f2)
***The left dashed line***
FromDevice(vEth1)->[2]switch[3]->Queue->ToDevice(enp6s0f1) 
FromDevice(enp6s0f1)->[3]switch[2]->Queue->ToDevice(vEth1)

The VNF configuration is simple forwarder for testing:

FromDevice(enp6s0f2)->Queue->ToDevice(vEth1)

Obviously this configuration did not work and Im trying to figure out, where is my misunderstanding

tbarbette commented 3 years ago

All ports have 2 sides, rx and tx. FromDevice is rx and ToDevice is tx for a real device, a real NIC.

KernelTap includes both at once for a virtual Tap. On the left of kernel tap you handle the tx and on the right the rx. There should be no Idle().

p4pe commented 3 years ago

Thanks for the clarification. This is my only mistake, Ι'm impressed :P ?

ahenning commented 3 years ago

@p4pe when a packet is received on a port on the switch, the switch uses the destination mac address to decide which port to switch the packet to (if the MAC is known). In your config, the packets would not take the VNF path by it self. Connecting devices to ports does not tell the switch how to switch the packets. I am assuming this is what you want to engineer with the config, i.e to redirect traffic to the VNF. If not please let me know.

If yes, and you always want the traffic to be sent to the VNF, and it will only be in the direction as per the diagram, then you don't need the complexity of the EtherSwitch. You can simply pump the packet to the KernelTap and assuming the VNF returns the packets, take the packets from KernelTap and send it out the interface you want. In that scenario the switch is not needed. from_eth0 -> [0]tap[0] -> to_eth1

Of course the L2 headers will need to be set, but that is the path to match the diagram. If the diagram is accurate, I would recommend to remove the switch.

The second scenario is that maybe some packets need to be sent to the VNF and some packets will be sent straight through the switch. This is more complex. In this scenario the switch makes sense, but how will switch know where to send the packets to? The L2 headers need to be set to determine where the packets will be switched to.

Here is another example config with a switch and KernelTap from_eth0 -> [0]switch[0] -> to_eth0; from_eth1 -> [1]switch[1] -> to_eth1; tap[0] -> [2]switch[2] -> [0]tap;

So if you want to use the switch to redirect packets to the VNF you need to do that redirect on the L2 header. In other words, lets say your KernelTap MAC address is 01:01:01:01:01:01 then before the packets are sent to the switch, the L2 header info needs to be changed so that the destination of the packets are 01:01:01:01:01. The destination in L2 header tells the switch which port the packets will be sent out. Assuming you want to redirect the traffic to the VNF, in the above config that would be port 2.

If bidirectional traffic is needed, then the EtherSwitch will make sense. Again either the traffic needs to be redirected with EtherEncap or the L2 headers needs to be set at the Source and Sink so that the L2 info tells the switch to send the packets to the tap port.

p4pe commented 3 years ago

Thank you for the answer @ahenning , yes I want the traffic to be redirected to the VNF, and then to a second VNF before goes to the sink.

I already did your first suggestion, but I was asked to do the second one with the EtherSwitch acting as an virtual switch(I was also tried a scenario with OVS and docker, but this killed the throughput). I do not need bidirectional traffic(yet), I want just to steer the traffic as I show in the figure.

Something that I forgot to say(and write in the config) is that all interfaces are on promisc mode.

tbarbette commented 3 years ago

Start with no VF, then one VF, then two :p Also if you're not handling backward traffic, then the EtherSwitch will probably not be able to learn MACs, so the switch won't work? So you should maybe hard-wire your configuration (you use no switching element, just Click edges): from_eth0->tap0; tap0->tap1; tap1->to_eth0;

p4pe commented 3 years ago

The no VF scenario is working :P I already did this with the hard-wired approach.. (but my supervisor wants the switch...)

p4pe commented 3 years ago

Following your advice @tbarbette (I convinced my supervisor :P) not to use the etherswitch. So I have the following setup

chain

For my NAT configuration:

FromDevice(enp6s0f1)
-> Strip(14)
-> CheckIPHeader()
->rw::IPRewriter(pattern 27.32.11.3 1024-65535 - - 0 1);
rw[0]->Queue->ToDevice(vEth1);
rw[1] -> Discard;

For my IPSec:

tap :: KernelTap(192.168.5.0/24, DEVNAME vEth1)
-> c::Counter
-> Strip(14)
-> CheckIPHeader()
-> ipsec_rt::RadixIPsecLookup( 10.10.1.1/32 0,
27.32.11.3/32 10.10.2.1 1 111 1111111111111111 1111111111111111 1 0 ,
0.0.0.0/0 2
);
ipsec_rt[0]
// Receive
-> Discard;
ipsec_rt[1]
-> IPsecESPEncap()
-> IPsecAuthHMACSHA1(0)
-> IPsecAES(1)
-> UDPIPEncap(10.10.1.1 , 4500 , 192.168.2.7 , 4500)
-> EtherEncap(0x800,3c:fd:fe:05:7a:80, 3c:fd:fe:04:a4:42)
-> Queue
-> c1::Counter
-> out::ToDevice(enp6s0f1);
ipsec_rt[2]
// Default
-> Discard;
Script(wait 1, print c1.rate, loop);

But it seems that the 27.32.11.3/32 register in the radixipseclookup table does not working. Why?

tbarbette commented 3 years ago

I've never used RadixIPsecLookup, and actually IPsec. You're sure you want that one and not RadixIPLookup? What are those supplementary parameters? I know the second term should be the port, then eventually the gateway. But those "1111111111" ?

BTW, you probably want "SNIFFING false" to FromDevice, as you don't want packets to continue in the IP stack.

ahenning commented 3 years ago

@p4pe

As Tom mentioned previously:

All ports have 2 sides, rx and tx. FromDevice is rx and ToDevice is tx for a real device, a real NIC.

Using these elements on virtual interfaces might not work so I recommend to use real NIC to save a lot of trouble.

Also I would again recommend to use Print and IPPrint elements to trace the packets. E.g. here the suspect is the Ipsec config but I doubt the packets are even passing through the KernelTap interface.

Here is an example of chaining separate click configs together: On the Linux host: ip link add veth0 type veth peer name veth1 Make sure the virtual interfaces are up

Source: FastUDPSource(1, 1000, 600, 00:0c:29:92:68:92, 192.168.32.128, 1234, 04:04:04:04:04:05, 192.168.32.132, 1234) -> Print("source") -> ToDevice(veth0);

Sink: FromDevice(veth1, PROMISC true) -> Print("fd sink") -> Discard;

p4pe commented 3 years ago

Hello, thank you very much for the directions!