blob: 2baf37cf0f25f356e01106f9271a84517a4a8571 [file] [log] [blame]
alshabibd6be76e2016-03-01 22:21:00 -08001import oftest.base_tests as base_tests
alshabibcde318b2016-03-01 22:49:13 -08002from oftest import config
alshabibd6be76e2016-03-01 22:21:00 -08003import ofp
Admin4ebddb82016-03-01 22:36:30 -08004from oftest.testutils import *
alshabibcde318b2016-03-01 22:49:13 -08005from oltconstants import *
6import copy
Admin44f754e2016-03-01 23:00:46 -08007import logging
Zsolt Harasztid0571402016-03-02 18:40:50 -08008from IGMP import IGMPv3, IGMPv3gr, IGMP_TYPE_MEMBERSHIP_QUERY, \
9 IGMP_TYPE_V3_MEMBERSHIP_REPORT, IGMP_V3_GR_TYPE_EXCLUDE, \
10 IGMP_V3_GR_TYPE_INCLUDE
alshabibd6be76e2016-03-01 22:21:00 -080011
alshabibcde318b2016-03-01 22:49:13 -080012
alshabibd6be76e2016-03-01 22:21:00 -080013class OltBaseTest(base_tests.SimpleDataPlane):
14
Zsolt Harasztif5c6aba2016-03-02 11:41:22 -080015 next_cookie_block = 40
16
17 def getCookieBlock(self):
18 """Returns the starting value of the next 100 cookies"""
19 c = self.next_cookie_block
20 OltBaseTest.next_cookie_block += 100
21 return c
22
23 def resetOlt(self):
24 """Reset the OLT into a clean healthy state"""
25 delete_all_flows(self.controller)
Zsolt Harasztid0571402016-03-02 18:40:50 -080026 delete_all_groups(self.controller)
Zsolt Harasztif5c6aba2016-03-02 11:41:22 -080027 do_barrier(self.controller)
28 verify_no_errors(self.controller)
29
alshabibcde318b2016-03-01 22:49:13 -080030 def testPacketIn(self, match, parsed_pkt):
31 delete_all_flows(self.controller)
32
33 pkt = str(parsed_pkt)
34
35 for of_port in config["port_map"]:
36 m = copy.deepcopy(match)
37 m.oxm_list.append(ofp.oxm.in_port(of_port))
38 request = ofp.message.flow_add(
39 table_id=test_param_get("table", 0),
40 cookie=42,
41 match=m,
42 instructions=[
43 ofp.instruction.apply_actions(
44 actions=[
45 ofp.action.output(
46 port=ofp.OFPP_CONTROLLER,
47 max_len=ofp.OFPCML_NO_BUFFER)])],
48 buffer_id=ofp.OFP_NO_BUFFER,
49 priority=1000)
50 logging.info("Inserting flow sending matching packets to controller")
51 self.controller.message_send(request)
52 do_barrier(self.controller)
53
alshabib5339b7d2016-03-01 23:14:57 -080054 for of_port in config["port_map"]:
55 logging.info("PacketInExact test, port %d", of_port)
56 self.dataplane.send(of_port, pkt)
57 verify_packet_in(self, pkt, of_port, ofp.OFPR_ACTION)
58 verify_packets(self, pkt, [])
alshabibcde318b2016-03-01 22:49:13 -080059
Zsolt Haraszti5b8b39c2016-03-02 22:25:51 -080060 def installEapolRule(self, in_port=None, install=True):
61 in_port = onu_port if in_port is None else in_port
alshabibcde318b2016-03-01 22:49:13 -080062 match = ofp.match()
63 match.oxm_list.append(ofp.oxm.eth_type(0x888e))
64 match.oxm_list.append(ofp.oxm.in_port(in_port))
65 if install:
66 request = ofp.message.flow_add(
67 table_id=test_param_get("table", 0),
68 cookie=42,
69 match=match,
70 instructions=[
71 ofp.instruction.apply_actions(
72 actions=[
73 ofp.action.output(
74 port=ofp.OFPP_CONTROLLER,
75 max_len=ofp.OFPCML_NO_BUFFER)])],
76 buffer_id=ofp.OFP_NO_BUFFER,
77 priority=1000)
78 else:
79 request = ofp.message.flow_delete(
80 table_id=test_param_get("table", 0),
81 cookie=42,
82 match=match,
83 instructions=[
84 ofp.instruction.apply_actions(
85 actions=[
86 ofp.action.output(
87 port=ofp.OFPP_CONTROLLER,
88 max_len=ofp.OFPCML_NO_BUFFER)])],
89 buffer_id=ofp.OFP_NO_BUFFER,
90 priority=1000)
91 logging.info("%s flow sending matching packets to controller" % "Install" if install else "Remove")
92 self.controller.message_send(request)
93 do_barrier(self.controller)
94
Zsolt Haraszti5b8b39c2016-03-02 22:25:51 -080095 def sendEapolIn(self, in_port=None):
96 """Send in an EAPOL frame and verify that the controller receives it"""
97
98 in_port = onu_port if in_port is None else in_port
99 pkt = str(simple_eth_packet(eth_dst='01:00:5e:7f:ff:ff', eth_type=0x888e, pktlen=60))
100 self.dataplane.send(in_port, pkt)
101 verify_packet_in(self, pkt, in_port, ofp.OFPR_ACTION)
102 verify_packets(self, pkt, [])
103
Zsolt Harasztif5c6aba2016-03-02 11:41:22 -0800104 def testPacketFlow(self,
105 s_vlan_id,
106 c_vlan_id,
107 onu=None,
108 verify_blocked_flows=True,
109 pktlen=96):
alshabibcde318b2016-03-01 22:49:13 -0800110
Zsolt Harasztif5c6aba2016-03-02 11:41:22 -0800111 incorrectTagPkt = simple_udp_packet(
112 pktlen=pktlen+4, dl_vlan_enable=True, vlan_vid=s_vlan_id, vlan_pcp=1)
113 zeroTaggedPkt = simple_udp_packet(
114 pktlen=pktlen+4, dl_vlan_enable=True, vlan_vid=0, vlan_pcp=0)
115 untaggedPkt = simple_udp_packet(pktlen=pktlen)
116 upstreamDoubleTaggedPkt = double_vlan_udp_packet(
117 pktlen=pktlen+8, dl_vlan_enable=True, c_vlan_vid=c_vlan_id, s_vlan_vid=s_vlan_id,
118 c_vlan_pcp=0, s_vlan_pcp=0)
alshabibcde318b2016-03-01 22:49:13 -0800119
alshabib5339b7d2016-03-01 23:14:57 -0800120 inport = onu_port if onu is None else onu
121
alshabibcde318b2016-03-01 22:49:13 -0800122 logging.info("Testing s-tag %d, c-tag %d" % (s_vlan_id, c_vlan_id))
123
124 # test upstream untagged packet got double tag at OLT
alshabib5339b7d2016-03-01 23:14:57 -0800125 self.dataplane.send(inport, str(zeroTaggedPkt))
Admin44f754e2016-03-01 23:00:46 -0800126 verify_packet(self, upstreamDoubleTaggedPkt, olt_port)
alshabibcde318b2016-03-01 22:49:13 -0800127
128 # test downstream doubletagged packet got untagged at ONU
129 self.dataplane.send(olt_port, str(upstreamDoubleTaggedPkt))
130 if device_type == "pmc":
alshabib5339b7d2016-03-01 23:14:57 -0800131 verify_packet(self, zeroTaggedPkt, inport)
alshabibcde318b2016-03-01 22:49:13 -0800132 else:
alshabib5339b7d2016-03-01 23:14:57 -0800133 verify_packet(self, untaggedPkt, inport)
alshabibcde318b2016-03-01 22:49:13 -0800134
Zsolt Harasztif5c6aba2016-03-02 11:41:22 -0800135 if verify_blocked_flows:
136 # test upstream doubletagged packet got dropped
137 self.dataplane.send(inport, str(upstreamDoubleTaggedPkt))
138 verify_no_packet(self, upstreamDoubleTaggedPkt, olt_port)
alshabibcde318b2016-03-01 22:49:13 -0800139
Zsolt Harasztif5c6aba2016-03-02 11:41:22 -0800140 # test downstream untagged packet got dropped at ONU
141 self.dataplane.send(olt_port, str(untaggedPkt))
142 verify_no_packet(self, untaggedPkt, inport)
alshabibcde318b2016-03-01 22:49:13 -0800143
Zsolt Harasztif5c6aba2016-03-02 11:41:22 -0800144 # test upstream icorrectly tagged packet; should get dropped
145 self.dataplane.send(inport, str(incorrectTagPkt))
146 verify_no_packet(self, upstreamDoubleTaggedPkt, olt_port)
147
148 def testSustainedPacketFlow(self, s_vlan_id, c_vlan_id, number_of_roundtrips=10, onu=None):
149 for i in xrange(number_of_roundtrips):
Zsolt Harasztid0571402016-03-02 18:40:50 -0800150 print "pkt # %d" % (i+1,)
Zsolt Harasztif5c6aba2016-03-02 11:41:22 -0800151 self.testPacketFlow(s_vlan_id, c_vlan_id, onu=onu, verify_blocked_flows=False)
alshabibcde318b2016-03-01 22:49:13 -0800152
alshabib5339b7d2016-03-01 23:14:57 -0800153 def installDoubleTaggingRules(self, s_vlan_id, c_vlan_id, cookie=42, onu = None):
154
155 inport = onu_port if onu is None else onu
alshabibd6be76e2016-03-01 22:21:00 -0800156
157 # upstream flow rule
158 match = ofp.match()
alshabib5339b7d2016-03-01 23:14:57 -0800159 match.oxm_list.append(ofp.oxm.in_port(inport))
alshabibd6be76e2016-03-01 22:21:00 -0800160 if device_type == "cpqd":
161 match.oxm_list.append(ofp.oxm.vlan_vid(value=ofp.OFPVID_NONE))
162 actions = [
163 ofp.action.push_vlan(ethertype=0x8100),
164 ofp.action.set_field(ofp.oxm.vlan_vid(ofp.OFPVID_PRESENT | c_vlan_id)),
165 ofp.action.set_field(ofp.oxm.vlan_pcp(0))
166 ]
167 else: # "pmc", "normal"
168 match.oxm_list.append(ofp.oxm.vlan_vid(value=ofp.OFPVID_PRESENT))
169 match.oxm_list.append(ofp.oxm.vlan_pcp(value=0))
170 actions = [
171 ofp.action.set_field(ofp.oxm.vlan_vid(ofp.OFPVID_PRESENT | c_vlan_id))
172 ]
173 cookie += 1
174
175 # push inner vlan (c-vlan) for upstream
176 request = ofp.message.flow_add(
177 table_id=test_param_get("table", 0),
178 cookie=cookie,
179 match=match,
180 instructions=[
181 ofp.instruction.apply_actions(actions=actions),
182 ofp.instruction.goto_table(1)],
183 buffer_id=ofp.OFP_NO_BUFFER,
184 priority=1000)
185
186 self.controller.message_send(request)
187 do_barrier(self.controller)
188 verify_no_errors(self.controller)
189
190 # push outer vlan (s-vlan) for upstream
191 match = ofp.match()
alshabib5339b7d2016-03-01 23:14:57 -0800192 match.oxm_list.append(ofp.oxm.in_port(inport))
alshabibd6be76e2016-03-01 22:21:00 -0800193 match.oxm_list.append(ofp.oxm.vlan_vid(ofp.OFPVID_PRESENT | c_vlan_id))
194 match.oxm_list.append(ofp.oxm.vlan_pcp(0))
195 cookie += 1
196
197 request = ofp.message.flow_add(
198 table_id=test_param_get("table", 1),
199 cookie=cookie,
200 match=match,
201 instructions=[
202 ofp.instruction.apply_actions(
203 actions=[
204 ofp.action.push_vlan(ethertype=0x8100),
205 ofp.action.set_field(ofp.oxm.vlan_vid(ofp.OFPVID_PRESENT | s_vlan_id)),
206 ofp.action.set_field(ofp.oxm.vlan_pcp(0)),
207 ofp.action.output(port=olt_port)]),
208 ],
209 buffer_id=ofp.OFP_NO_BUFFER,
210 priority=1000)
211
212 self.controller.message_send(request)
213 do_barrier(self.controller)
214 verify_no_errors(self.controller)
215 cookie += 1
216
217 # strip outer vlan (s-vlan) for downstream
218 match = ofp.match()
219 match.oxm_list.append(ofp.oxm.in_port(olt_port))
220 match.oxm_list.append(ofp.oxm.vlan_vid(ofp.OFPVID_PRESENT | s_vlan_id))
221 match.oxm_list.append(ofp.oxm.vlan_pcp(0))
222 request = ofp.message.flow_add(
223 table_id=test_param_get("table", 0),
224 cookie=cookie,
225 match=match,
226 instructions=[
227 ofp.instruction.apply_actions(
228 actions=[ofp.action.pop_vlan()]),
229 ofp.instruction.goto_table(1)],
230 buffer_id=ofp.OFP_NO_BUFFER,
231 priority=1000)
232
233 self.controller.message_send(request)
234 do_barrier(self.controller)
235 verify_no_errors(self.controller)
236
237 # rewrite inner vlan (c-vlan) to default (0) for downstream
238 match = ofp.match()
239 match.oxm_list.append(ofp.oxm.in_port(olt_port))
240 match.oxm_list.append(ofp.oxm.vlan_vid(ofp.OFPVID_PRESENT | c_vlan_id))
241 match.oxm_list.append(ofp.oxm.vlan_pcp(0))
242 cookie += 1
243
244 request = ofp.message.flow_add(
245 table_id=test_param_get("table", 1),
246 cookie=cookie,
247 match=match,
248 instructions=[
249 ofp.instruction.apply_actions(
250 actions=[
251 ofp.action.pop_vlan(),
alshabib5339b7d2016-03-01 23:14:57 -0800252 ofp.action.output(port=inport)])
alshabibd6be76e2016-03-01 22:21:00 -0800253 ],
254 buffer_id=ofp.OFP_NO_BUFFER,
255 priority=1000)
256
257 self.controller.message_send(request)
258 do_barrier(self.controller)
259 verify_no_errors(self.controller)
Zsolt Harasztid0571402016-03-02 18:40:50 -0800260
Zsolt Haraszti5b8b39c2016-03-02 22:25:51 -0800261 def installIgmpRule(self, cookie):
Zsolt Harasztid0571402016-03-02 18:40:50 -0800262 """Etsablish flow rules that will forward any incoming
263 IGMP packets to controller (from any port, OLT and ONUs)
264 """
265 match = ofp.match()
266 match.oxm_list.append(ofp.oxm.eth_type(0x800))
267 match.oxm_list.append(ofp.oxm.ip_proto(2))
268 request = ofp.message.flow_add(
269 table_id=test_param_get("table", 0),
270 cookie=42,
271 match=match,
272 instructions=[
273 ofp.instruction.apply_actions(
274 actions=[
275 ofp.action.output(
276 port=ofp.OFPP_CONTROLLER,
277 max_len=ofp.OFPCML_NO_BUFFER)])],
278 buffer_id=ofp.OFP_NO_BUFFER,
279 priority=2000)
280 logging.info("Inserting flow sending matching packets to controller")
281 self.controller.message_send(request)
282 do_barrier(self.controller)
283
284 def testIgmpQueryOut(self, onu=None):
285 """Send an IGMP Query out to given onu and verify that it arrives"""
286
287 outport = onu_port if onu is None else onu
288
289 igmp = IGMPv3() # by default this is a query
290 pkt = self.buildIgmp(igmp)
291
292 msg = ofp.message.packet_out(
293 in_port=ofp.OFPP_CONTROLLER,
294 actions=[ofp.action.output(port=outport)],
295 buffer_id=ofp.OFP_NO_BUFFER,
296 data=str(pkt))
297
298 self.controller.message_send(msg)
299
300 rv = self.controller.message_send(msg)
301 self.assertTrue(rv == 0, "Error sending output message")
302 verify_no_errors(self.controller)
303
304 verify_packet(self, pkt, outport)
305
306 def sendIgmpReport(self, join=[], leave=[], onu=None, srcmac=None,
307 ip_src='1.2.3.4', ip_dst='2440.0.0.22', pad_to=None):
308 """Send an IGMP Join request from given onu to given mcast group"""
309
310 # construct packet
311 in_port = onu_port if onu is None else onu
312 src_mac = '00:00:00:00:be:ef' if srcmac is None else srcmac
313 pkt = str(self.buildIgmpReport(ip_src, ip_dst, src_mac, join, leave, pad_to=pad_to))
314
315 # send packet via in_port
316 self.dataplane.send(in_port, pkt)
317
318 # verify it is received by controller and not by some other port
319 verify_packet_in(self, pkt, in_port, ofp.OFPR_ACTION)
320 verify_packets(self, pkt, [])
321
322 def buildIgmpReport(self, ip_src, ip_dst, srcmac, join=[], leave=[], sources={}, pad_to=None):
323 """ Return an IGMPv4 Membership Report as a scapy Ethernet frame
324 join: a list of multicast addresses that shall be joined
325 leave: a list of multicast addresses that shall be left
326 sources: a dict of source lists keyed by the mcast address that needs to be joined
327 or left.
328 """
329
330 assert join or leave, "Either join or leave must be a non-empty list"
331
332 igmp = IGMPv3(type=IGMP_TYPE_V3_MEMBERSHIP_REPORT, max_resp_code=30, gaddr="224.0.0.1")
333
334 for mcast_group in join:
335 srcs = sources.get(mcast_group, [])
336 if len(srcs):
337 gr = IGMPv3gr(rtype=IGMP_V3_GR_TYPE_INCLUDE, mcaddr=mcast_group)
338 gr.sources = srcs
339 else:
340 gr = IGMPv3gr(rtype=IGMP_V3_GR_TYPE_EXCLUDE, mcaddr=mcast_group)
341 igmp.grps.append(gr)
342
343 for mcast_group in leave:
344 gr = IGMPv3gr(rtype=IGMP_V3_GR_TYPE_INCLUDE, mcaddr=mcast_group)
345 igmp.grps.append(gr)
346
347 pkt = IGMPv3.fixup( scapy.Ether(src=srcmac) / scapy.IP() / igmp )
348 pkt = self.padPktTo(pkt, pad_to)
349 return pkt
350
351 def buildIgmp(self, payload, pad_to=None):
352 pkt = IGMPv3.fixup(scapy.Ether() / scapy.IP() / payload)
353 return self.padPktTo(pkt, pad_to)
354
355 def padPktTo(self, pkt, pad_to=None):
356 """If pad_to is provided, it shall be an integer. If pkt is smaller than that number,
357 it will be properly padded (ethernet padding) and returned. Otherwise the original
358 pkt is returned.
359 """
360 if pad_to is None:
361 return pkt
362
363 pad_len = pad_to - len(pkt)
364 if pad_len > 0:
365 pad = scapy.scapy.layers.l2.Padding()
366 pad.load = '\x00' * pad_len
367 pkt = pkt / pad
368 return pkt
369
370 def setupMcastChannel(self, mcast_addr, mcast_vlan_id, port_list, group_id=1, cookie=100):
371 """Setup multicast forwarding for the mcast address using mcast vlan id
372 and given port list.
373 """
374
375 # setup group first with given port list
376
377 buckets = [
378 ofp.common.bucket(
379 watch_port=ofp.OFPP_ANY,
380 watch_group=ofp.OFPG_ANY,
381 actions=[
382 ofp.action.pop_vlan(),
383 ofp.action.output(port=port)
384 ])
385 for port in port_list
386 ]
387
388 msg = ofp.message.group_add(
389 group_type=ofp.OFPGT_ALL,
390 group_id=group_id,
391 buckets=buckets
392 )
393
394 self.controller.message_send(msg)
395 do_barrier(self.controller)
396 verify_no_errors(self.controller)
397
398 # Then setup flow rule pointing to group
399
400 match = ofp.match()
401 match.oxm_list.append(ofp.oxm.in_port(olt_port))
402 match.oxm_list.append(ofp.oxm.vlan_vid(ofp.OFPVID_PRESENT | mcast_vlan_id))
403 match.oxm_list.append(ofp.oxm.eth_type(0x800))
404 match.oxm_list.append(ofp.oxm.ipv4_dst(self.ip2int(mcast_addr)))
405 request = ofp.message.flow_add(
406 table_id=test_param_get("table", 0),
407 cookie=cookie,
408 match=match,
409 instructions=[
410 ofp.instruction.apply_actions(
411 actions=[ofp.action.group(group_id=group_id)]),
412 ],
413 buffer_id=ofp.OFP_NO_BUFFER, priority=1000)
414 self.controller.message_send(request)
415 do_barrier(self.controller)
416 verify_no_errors(self.controller)
417
418 def removeMcastChannel(self, mcast_addr, mcast_vlan_id, port_list, group_id, cookie):
419 """Remove mumticast forwarding for given mcast address"""
420
421 # Remove flow first
422 match = ofp.match()
423 match.oxm_list.append(ofp.oxm.in_port(olt_port))
424 match.oxm_list.append(ofp.oxm.vlan_vid(ofp.OFPVID_PRESENT | mcast_vlan_id))
425 match.oxm_list.append(ofp.oxm.eth_type(0x800))
426 match.oxm_list.append(ofp.oxm.ipv4_dst(self.ip2int(mcast_addr)))
427 request = ofp.message.flow_delete(
428 table_id=test_param_get("table", 0),
429 cookie=cookie,
430 match=match,
431 instructions=[
432 ofp.instruction.apply_actions(
433 actions=[ofp.action.group(group_id=group_id)]),
434 ],
435 buffer_id=ofp.OFP_NO_BUFFER, priority=1000)
436 self.controller.message_send(request)
437 do_barrier(self.controller)
438 verify_no_errors(self.controller)
439
440 # Then remove the group
441 group_delete = ofp.message.group_delete(group_id=group_id)
442 self.controller.message_send(group_delete)
443 do_barrier(self.controller)
444 verify_no_errors(self.controller)
445
446 def ip2int(self, ip):
447 """Convert a dot-notated string IP address"""
448 digits = [int(d) for d in ip.split('.')]
449 assert len(digits) == 4
450 val = (
451 (digits[0] & 0xff) << 24 |
452 (digits[1] & 0xff) << 16 |
453 (digits[2] & 0xff) << 8 |
454 (digits[3] & 0xff))
455 return val
456
457 def mcastIp2McastMac(self, ip):
458 """ Convert a dot-notated IPv4 multicast address string into an multicast MAC address"""
459 digits = [int(d) for d in ip.split('.')]
460 return '01:00:5e:%02x:%02x:%02x' % (digits[1] & 0x7f, digits[2] & 0xff, digits[3] & 0xff)
461
462 def testMcastFlow(self, mcast_addr, mcast_vlan_id, ports=None, numpkt=1, ip_src="66.77.88.99",
463 expect_to_be_blocked=False):
464 """ Send given number of mcast packets using mcast address and vlan_id
465 and check they arrive to given port(s).
466 """
467
468 # construct mcast packet
469 pktlen = 250
470 pktToSend = simple_udp_packet(
471 eth_dst=self.mcastIp2McastMac(mcast_addr),
472 ip_src=ip_src,
473 ip_dst=mcast_addr,
474 pktlen=pktlen+4,
475 dl_vlan_enable=True,
476 vlan_vid=mcast_vlan_id,
477 vlan_pcp=0)
478
479 if device_type == "pmc":
480 pktToReceive = simple_udp_packet(
481 eth_dst=self.mcastIp2McastMac(mcast_addr),
482 ip_src=ip_src,
483 ip_dst=mcast_addr,
484 pktlen=pktlen+4,
485 dl_vlan_enable=True,
486 vlan_vid=0,
487 vlan_pcp=0)
488 else:
489 pktToReceive = simple_udp_packet(
490 eth_dst=self.mcastIp2McastMac(mcast_addr),
491 ip_src=ip_src,
492 ip_dst=mcast_addr,
493 pktlen=pktlen)
494
495 # send mcast packet to olt
496 self.dataplane.send(olt_port, str(pktToSend))
497
498 # test that packet is received on each designated port
499 ports = [onu_port] if ports is None else ports
500 for port in ports:
501 if expect_to_be_blocked:
502 verify_no_packet(self, pktToReceive, port)
503 else:
504 verify_packet(self, pktToReceive, port)