It takes time for a flow entry to be created in response to a Packet-In sent by a packet destined for a broadcast address or an unknown address. If another packet is sent to the same address before the entry is set, the controller will try to INSERT twice and get an error. A typical case would be two broadcast packets sent in succession from a host. This is a normal situation, such as ARP Requests for two hosts.
There are many possible workarounds, but this section will give you the simplest workaround. In other words, when adding a flow entry, if double registration occurs, the error is ignored.
Below is the Python code that sends two broadcast packets in a row. You can also adjust the sleep() time in your code to see how long it takes to complete single flow entry. As you can see from the destination interface and srcMac, this program produces packets equivalent to those sent from the h1 host.
import socket
import time
iface = 'h1-eth0'
dstMac = b'\xff\xff\xff\xff\xff\xff'
srcMac = b'\x00\x00\x00\x00\x00\x01'
proto = b'\x88\xb5' # protocol type is IEEE Local Experimental
packet = dstMac + srcMac + proto + "012345678901234567890123456789012345678901234567890123456789"
s = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(3)) # 3:ETH_P_ALL
s.bind((iface, 0))
print('send #1 packet.')
s.send(packet)
time.sleep(1)
print('send #2 packet.')
s.send(packet)
print('done.')
s.close()
Follow the nanosw05 tutorial to run the Mininet / P4Runtime Shell in the same environment. Then, place the above program under /root in the Mininet environment and execute it as follows.
mininet> h1 python double_send.py
send #1 packet. <<< 1 second wait time after this display appears
send #2 packet.
done.
mininet>
このパケットがただしく他のホスト、つまり h2, h3 に転送されていることを確認するには、h2 あるいは h3 側でモニタリングする必要があります。(モニタリング操作については NanoSwitch01 を参照してください。)
Since there is no change in the switch program this time, nanosw05.p4 is used as it is.
When you execute the double_send.py program as above, you can see the following messages. The arrival of the first packet created a flow entry, with match pattern of broadcast destination and h1 (00: 00: 00: 00: 00: 01) source. The second packet sent 1 sec later is processed according to flow entry, it will not Packet-In. You can see the packets wrapped in the switch by monitoring the h2 and h3 interfaces.
P4Runtime sh >>> PacketIn()
.......
======
packet-in: dst=ff:ff:ff:ff:ff:ff src=00:00:00:00:00:01 port=1
broadcast!
## INSERT ## dst=ff:ff:ff:ff:ff:ff src=00:00:00:00:00:01 port=65535
## set flooding action
macTable (mac - port)
00:00:00:00:00:01 - port(1)
......
In this state, modify the code of double_send.py so that the second packet is sent without waiting. You can comment out the sleep() line as follows:
print('send #1 packet.')
s.send(packet)
# time.sleep(1) <<<< commented out
print('send #2 packet.')
s.send(packet)
Use this to perform the send operation again on Mininet.
mininet> h1 python double_send.py
send #1 packet. <<< no wait time
send #2 packet.
done.
mininet>
This time you will get the following error and the P4Runtime Shell will stop.
P4Runtime sh >>> PacketIn()
======
packet-in: dst=ff:ff:ff:ff:ff:ff src=00:00:00:00:00:01 port=1 <<< 1st packet arrived
broadcast!
## INSERT ## dst=ff:ff:ff:ff:ff:ff src=00:00:00:00:00:01 port=65535
## set flooding action <<<< set the flow entry
macTable (mac - port)
00:00:00:00:00:02 - port(2)
00:00:00:00:00:01 - port(1)
.
======
packet-in: dst=ff:ff:ff:ff:ff:ff src=00:00:00:00:00:01 port=1 <<< 2nd packet arrived
broadcast!
## INSERT ## dst=ff:ff:ff:ff:ff:ff src=00:00:00:00:00:01 port=65535
## set flooding action <<<< try to set the flow entry again, get an error as follows;
---------------------------------------------------------------------------
P4RuntimeWriteException Traceback (most recent call last)
<ipython-input-1-9a9af7272159> in <module>
----> 1 PacketIn()
/p4runtime-sh/p4runtime_sh/shell.py in PacketIn()
2427 # print("\nResponse message is:")
2428 # print(rep)
-> 2429 packetin_process(rep)
2430 # return rep # if you want to check the response, just return
2431 except KeyboardInterrupt:
/p4runtime-sh/p4runtime_sh/shell.py in packetin_process(pin)
2389 if srcMac not in macTable:
2390 macTable[ srcMac ] = port
-> 2391 insertFlowEntry(dstMac, srcMac, FLOOD_PORT)
2392 PacketOut(port, FLOOD_GRP, payload)
2393 else: # when destination is unicast, do forwarding
/p4runtime-sh/p4runtime_sh/shell.py in insertFlowEntry(dstMac, srcMac, port)
2351
2352 # print(req)
-> 2353 client.write(req)
2354
2355 # macTable = [ mac: port ] - store mac and port of source host
/p4runtime-sh/p4runtime_sh/p4runtime.py in handle(*args, **kwargs)
122 if e.code() != grpc.StatusCode.UNKNOWN:
123 raise e
--> 124 raise P4RuntimeWriteException(e) from None
125 return handle
126
P4RuntimeWriteException: Error(s) during Write:
* At index 0: ALREADY_EXISTS, 'Match entry exists, use MODIFY if you wish to change action'
If you check the flow entry content just to be sure, it has been set to flooding for broadcast packet is registered correctly.
P4Runtime sh >>> PrintTable()
MyIngress.l2_match_table
dst=ff:ff:ff:ff:ff:ff src=00:00:00:00:00:01 action=MyIngress.flooding
P4Runtime sh >>>
So this time, the second packet arrives at the switch earlier than the Packet-In processing of the first packet registration. Then, this second packet makes Packet-In again, and the process of registering the exact same flow entry twice is executed. However, the write() process for that failed and caused a P4RuntimeWriteException error.
There are many possible workarounds, but this section will give you the simplest workaround. In other words, when adding a flow entry, if double registration occurs, the error is ignored.
It is the insertFlowEntry () function in shell.py that raises the error. We want to correct the following parts. Originally it was the following single line.
def insertFlowEntry(dstMac, srcMac, port):
...(snip)...
client.write(req)
Add error handling processing here as follows.
try:
client.write(req)
except P4RuntimeWriteException as e:
if e.errors[0][1].canonical_code == code_pb2.ALREADY_EXISTS:
print("already exists. ignore this.")
else:
print(e)
raise e
In other words, for an error that is simply double-registered, just notice a message and ignore it. Except ALREADY_EXISTS error will directly raise as an error. To add this code, the following two lines are added at the beginning of shell.py.
from p4runtime_sh.p4runtime import P4RuntimeWriteException
from google.rpc import status_pb2, code_pb2
Now, use the modified shell.py (under nanosw06 directory) to restart the P4Runtime Shell and run double_send.py again on the Mininet. The following messages will be displayed, and it can be confirmed that the program does not stop even the double registration has occurred.
======
packet-in: dst=ff:ff:ff:ff:ff:ff src=00:00:00:00:00:01 port=1 <<<<< 1st packet arrived
broadcast!
## INSERT ## dst=ff:ff:ff:ff:ff:ff src=00:00:00:00:00:01 port=65535
## set flooding action <<<< set the flow entry
macTable (mac - port)
00:00:00:00:00:01 - port(1)
.
======
packet-in: dst=ff:ff:ff:ff:ff:ff src=00:00:00:00:00:01 port=1 <<<<< 2nd packet arrived
broadcast!
## INSERT ## dst=ff:ff:ff:ff:ff:ff src=00:00:00:00:00:01 port=65535
## set flooding action <<<< set the flow entry again
already exists. ignore this. <<<<< this time, error occurs but ignored. no stop.
macTable (mac - port)
00:00:00:00:00:01 - port(1)
.........
It's still incomplete as a switch, but I think we've covered what we should do as a P4-based switch with a controller.
You have completed the series of tutorials. Congratulations!
Is this good for the next?