blob: 11180544e3e2506990ce557066560d681c32ad2b [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 Harasztid0571402016-03-02 18:40:50 -080060 def processEapolRule(self, in_port, install=True):
alshabibcde318b2016-03-01 22:49:13 -080061 match = ofp.match()
62 match.oxm_list.append(ofp.oxm.eth_type(0x888e))
63 match.oxm_list.append(ofp.oxm.in_port(in_port))
64 if install:
65 request = ofp.message.flow_add(
66 table_id=test_param_get("table", 0),
67 cookie=42,
68 match=match,
69 instructions=[
70 ofp.instruction.apply_actions(
71 actions=[
72 ofp.action.output(
73 port=ofp.OFPP_CONTROLLER,
74 max_len=ofp.OFPCML_NO_BUFFER)])],
75 buffer_id=ofp.OFP_NO_BUFFER,
76 priority=1000)
77 else:
78 request = ofp.message.flow_delete(
79 table_id=test_param_get("table", 0),
80 cookie=42,
81 match=match,
82 instructions=[
83 ofp.instruction.apply_actions(
84 actions=[
85 ofp.action.output(
86 port=ofp.OFPP_CONTROLLER,
87 max_len=ofp.OFPCML_NO_BUFFER)])],
88 buffer_id=ofp.OFP_NO_BUFFER,
89 priority=1000)
90 logging.info("%s flow sending matching packets to controller" % "Install" if install else "Remove")
91 self.controller.message_send(request)
92 do_barrier(self.controller)
93
Zsolt Harasztif5c6aba2016-03-02 11:41:22 -080094 def testPacketFlow(self,
95 s_vlan_id,
96 c_vlan_id,
97 onu=None,
98 verify_blocked_flows=True,
99 pktlen=96):
alshabibcde318b2016-03-01 22:49:13 -0800100
Zsolt Harasztif5c6aba2016-03-02 11:41:22 -0800101 incorrectTagPkt = simple_udp_packet(
102 pktlen=pktlen+4, dl_vlan_enable=True, vlan_vid=s_vlan_id, vlan_pcp=1)
103 zeroTaggedPkt = simple_udp_packet(
104 pktlen=pktlen+4, dl_vlan_enable=True, vlan_vid=0, vlan_pcp=0)
105 untaggedPkt = simple_udp_packet(pktlen=pktlen)
106 upstreamDoubleTaggedPkt = double_vlan_udp_packet(
107 pktlen=pktlen+8, dl_vlan_enable=True, c_vlan_vid=c_vlan_id, s_vlan_vid=s_vlan_id,
108 c_vlan_pcp=0, s_vlan_pcp=0)
alshabibcde318b2016-03-01 22:49:13 -0800109
alshabib5339b7d2016-03-01 23:14:57 -0800110 inport = onu_port if onu is None else onu
111
alshabibcde318b2016-03-01 22:49:13 -0800112 logging.info("Testing s-tag %d, c-tag %d" % (s_vlan_id, c_vlan_id))
113
114 # test upstream untagged packet got double tag at OLT
alshabib5339b7d2016-03-01 23:14:57 -0800115 self.dataplane.send(inport, str(zeroTaggedPkt))
Admin44f754e2016-03-01 23:00:46 -0800116 verify_packet(self, upstreamDoubleTaggedPkt, olt_port)
alshabibcde318b2016-03-01 22:49:13 -0800117
118 # test downstream doubletagged packet got untagged at ONU
119 self.dataplane.send(olt_port, str(upstreamDoubleTaggedPkt))
120 if device_type == "pmc":
alshabib5339b7d2016-03-01 23:14:57 -0800121 verify_packet(self, zeroTaggedPkt, inport)
alshabibcde318b2016-03-01 22:49:13 -0800122 else:
alshabib5339b7d2016-03-01 23:14:57 -0800123 verify_packet(self, untaggedPkt, inport)
alshabibcde318b2016-03-01 22:49:13 -0800124
Zsolt Harasztif5c6aba2016-03-02 11:41:22 -0800125 if verify_blocked_flows:
126 # test upstream doubletagged packet got dropped
127 self.dataplane.send(inport, str(upstreamDoubleTaggedPkt))
128 verify_no_packet(self, upstreamDoubleTaggedPkt, olt_port)
alshabibcde318b2016-03-01 22:49:13 -0800129
Zsolt Harasztif5c6aba2016-03-02 11:41:22 -0800130 # test downstream untagged packet got dropped at ONU
131 self.dataplane.send(olt_port, str(untaggedPkt))
132 verify_no_packet(self, untaggedPkt, inport)
alshabibcde318b2016-03-01 22:49:13 -0800133
Zsolt Harasztif5c6aba2016-03-02 11:41:22 -0800134 # test upstream icorrectly tagged packet; should get dropped
135 self.dataplane.send(inport, str(incorrectTagPkt))
136 verify_no_packet(self, upstreamDoubleTaggedPkt, olt_port)
137
138 def testSustainedPacketFlow(self, s_vlan_id, c_vlan_id, number_of_roundtrips=10, onu=None):
139 for i in xrange(number_of_roundtrips):
Zsolt Harasztid0571402016-03-02 18:40:50 -0800140 print "pkt # %d" % (i+1,)
Zsolt Harasztif5c6aba2016-03-02 11:41:22 -0800141 self.testPacketFlow(s_vlan_id, c_vlan_id, onu=onu, verify_blocked_flows=False)
alshabibcde318b2016-03-01 22:49:13 -0800142
alshabib5339b7d2016-03-01 23:14:57 -0800143 def installDoubleTaggingRules(self, s_vlan_id, c_vlan_id, cookie=42, onu = None):
144
145 inport = onu_port if onu is None else onu
alshabibd6be76e2016-03-01 22:21:00 -0800146
147 # upstream flow rule
148 match = ofp.match()
alshabib5339b7d2016-03-01 23:14:57 -0800149 match.oxm_list.append(ofp.oxm.in_port(inport))
alshabibd6be76e2016-03-01 22:21:00 -0800150 if device_type == "cpqd":
151 match.oxm_list.append(ofp.oxm.vlan_vid(value=ofp.OFPVID_NONE))
152 actions = [
153 ofp.action.push_vlan(ethertype=0x8100),
154 ofp.action.set_field(ofp.oxm.vlan_vid(ofp.OFPVID_PRESENT | c_vlan_id)),
155 ofp.action.set_field(ofp.oxm.vlan_pcp(0))
156 ]
157 else: # "pmc", "normal"
158 match.oxm_list.append(ofp.oxm.vlan_vid(value=ofp.OFPVID_PRESENT))
159 match.oxm_list.append(ofp.oxm.vlan_pcp(value=0))
160 actions = [
161 ofp.action.set_field(ofp.oxm.vlan_vid(ofp.OFPVID_PRESENT | c_vlan_id))
162 ]
163 cookie += 1
164
165 # push inner vlan (c-vlan) for upstream
166 request = ofp.message.flow_add(
167 table_id=test_param_get("table", 0),
168 cookie=cookie,
169 match=match,
170 instructions=[
171 ofp.instruction.apply_actions(actions=actions),
172 ofp.instruction.goto_table(1)],
173 buffer_id=ofp.OFP_NO_BUFFER,
174 priority=1000)
175
176 self.controller.message_send(request)
177 do_barrier(self.controller)
178 verify_no_errors(self.controller)
179
180 # push outer vlan (s-vlan) for upstream
181 match = ofp.match()
alshabib5339b7d2016-03-01 23:14:57 -0800182 match.oxm_list.append(ofp.oxm.in_port(inport))
alshabibd6be76e2016-03-01 22:21:00 -0800183 match.oxm_list.append(ofp.oxm.vlan_vid(ofp.OFPVID_PRESENT | c_vlan_id))
184 match.oxm_list.append(ofp.oxm.vlan_pcp(0))
185 cookie += 1
186
187 request = ofp.message.flow_add(
188 table_id=test_param_get("table", 1),
189 cookie=cookie,
190 match=match,
191 instructions=[
192 ofp.instruction.apply_actions(
193 actions=[
194 ofp.action.push_vlan(ethertype=0x8100),
195 ofp.action.set_field(ofp.oxm.vlan_vid(ofp.OFPVID_PRESENT | s_vlan_id)),
196 ofp.action.set_field(ofp.oxm.vlan_pcp(0)),
197 ofp.action.output(port=olt_port)]),
198 ],
199 buffer_id=ofp.OFP_NO_BUFFER,
200 priority=1000)
201
202 self.controller.message_send(request)
203 do_barrier(self.controller)
204 verify_no_errors(self.controller)
205 cookie += 1
206
207 # strip outer vlan (s-vlan) for downstream
208 match = ofp.match()
209 match.oxm_list.append(ofp.oxm.in_port(olt_port))
210 match.oxm_list.append(ofp.oxm.vlan_vid(ofp.OFPVID_PRESENT | s_vlan_id))
211 match.oxm_list.append(ofp.oxm.vlan_pcp(0))
212 request = ofp.message.flow_add(
213 table_id=test_param_get("table", 0),
214 cookie=cookie,
215 match=match,
216 instructions=[
217 ofp.instruction.apply_actions(
218 actions=[ofp.action.pop_vlan()]),
219 ofp.instruction.goto_table(1)],
220 buffer_id=ofp.OFP_NO_BUFFER,
221 priority=1000)
222
223 self.controller.message_send(request)
224 do_barrier(self.controller)
225 verify_no_errors(self.controller)
226
227 # rewrite inner vlan (c-vlan) to default (0) for downstream
228 match = ofp.match()
229 match.oxm_list.append(ofp.oxm.in_port(olt_port))
230 match.oxm_list.append(ofp.oxm.vlan_vid(ofp.OFPVID_PRESENT | c_vlan_id))
231 match.oxm_list.append(ofp.oxm.vlan_pcp(0))
232 cookie += 1
233
234 request = ofp.message.flow_add(
235 table_id=test_param_get("table", 1),
236 cookie=cookie,
237 match=match,
238 instructions=[
239 ofp.instruction.apply_actions(
240 actions=[
241 ofp.action.pop_vlan(),
alshabib5339b7d2016-03-01 23:14:57 -0800242 ofp.action.output(port=inport)])
alshabibd6be76e2016-03-01 22:21:00 -0800243 ],
244 buffer_id=ofp.OFP_NO_BUFFER,
245 priority=1000)
246
247 self.controller.message_send(request)
248 do_barrier(self.controller)
249 verify_no_errors(self.controller)
Zsolt Harasztid0571402016-03-02 18:40:50 -0800250
251 def setupIgmpCaptureFlowRules(self, cookie):
252 """Etsablish flow rules that will forward any incoming
253 IGMP packets to controller (from any port, OLT and ONUs)
254 """
255 match = ofp.match()
256 match.oxm_list.append(ofp.oxm.eth_type(0x800))
257 match.oxm_list.append(ofp.oxm.ip_proto(2))
258 request = ofp.message.flow_add(
259 table_id=test_param_get("table", 0),
260 cookie=42,
261 match=match,
262 instructions=[
263 ofp.instruction.apply_actions(
264 actions=[
265 ofp.action.output(
266 port=ofp.OFPP_CONTROLLER,
267 max_len=ofp.OFPCML_NO_BUFFER)])],
268 buffer_id=ofp.OFP_NO_BUFFER,
269 priority=2000)
270 logging.info("Inserting flow sending matching packets to controller")
271 self.controller.message_send(request)
272 do_barrier(self.controller)
273
274 def testIgmpQueryOut(self, onu=None):
275 """Send an IGMP Query out to given onu and verify that it arrives"""
276
277 outport = onu_port if onu is None else onu
278
279 igmp = IGMPv3() # by default this is a query
280 pkt = self.buildIgmp(igmp)
281
282 msg = ofp.message.packet_out(
283 in_port=ofp.OFPP_CONTROLLER,
284 actions=[ofp.action.output(port=outport)],
285 buffer_id=ofp.OFP_NO_BUFFER,
286 data=str(pkt))
287
288 self.controller.message_send(msg)
289
290 rv = self.controller.message_send(msg)
291 self.assertTrue(rv == 0, "Error sending output message")
292 verify_no_errors(self.controller)
293
294 verify_packet(self, pkt, outport)
295
296 def sendIgmpReport(self, join=[], leave=[], onu=None, srcmac=None,
297 ip_src='1.2.3.4', ip_dst='2440.0.0.22', pad_to=None):
298 """Send an IGMP Join request from given onu to given mcast group"""
299
300 # construct packet
301 in_port = onu_port if onu is None else onu
302 src_mac = '00:00:00:00:be:ef' if srcmac is None else srcmac
303 pkt = str(self.buildIgmpReport(ip_src, ip_dst, src_mac, join, leave, pad_to=pad_to))
304
305 # send packet via in_port
306 self.dataplane.send(in_port, pkt)
307
308 # verify it is received by controller and not by some other port
309 verify_packet_in(self, pkt, in_port, ofp.OFPR_ACTION)
310 verify_packets(self, pkt, [])
311
312 def buildIgmpReport(self, ip_src, ip_dst, srcmac, join=[], leave=[], sources={}, pad_to=None):
313 """ Return an IGMPv4 Membership Report as a scapy Ethernet frame
314 join: a list of multicast addresses that shall be joined
315 leave: a list of multicast addresses that shall be left
316 sources: a dict of source lists keyed by the mcast address that needs to be joined
317 or left.
318 """
319
320 assert join or leave, "Either join or leave must be a non-empty list"
321
322 igmp = IGMPv3(type=IGMP_TYPE_V3_MEMBERSHIP_REPORT, max_resp_code=30, gaddr="224.0.0.1")
323
324 for mcast_group in join:
325 srcs = sources.get(mcast_group, [])
326 if len(srcs):
327 gr = IGMPv3gr(rtype=IGMP_V3_GR_TYPE_INCLUDE, mcaddr=mcast_group)
328 gr.sources = srcs
329 else:
330 gr = IGMPv3gr(rtype=IGMP_V3_GR_TYPE_EXCLUDE, mcaddr=mcast_group)
331 igmp.grps.append(gr)
332
333 for mcast_group in leave:
334 gr = IGMPv3gr(rtype=IGMP_V3_GR_TYPE_INCLUDE, mcaddr=mcast_group)
335 igmp.grps.append(gr)
336
337 pkt = IGMPv3.fixup( scapy.Ether(src=srcmac) / scapy.IP() / igmp )
338 pkt = self.padPktTo(pkt, pad_to)
339 return pkt
340
341 def buildIgmp(self, payload, pad_to=None):
342 pkt = IGMPv3.fixup(scapy.Ether() / scapy.IP() / payload)
343 return self.padPktTo(pkt, pad_to)
344
345 def padPktTo(self, pkt, pad_to=None):
346 """If pad_to is provided, it shall be an integer. If pkt is smaller than that number,
347 it will be properly padded (ethernet padding) and returned. Otherwise the original
348 pkt is returned.
349 """
350 if pad_to is None:
351 return pkt
352
353 pad_len = pad_to - len(pkt)
354 if pad_len > 0:
355 pad = scapy.scapy.layers.l2.Padding()
356 pad.load = '\x00' * pad_len
357 pkt = pkt / pad
358 return pkt
359
360 def setupMcastChannel(self, mcast_addr, mcast_vlan_id, port_list, group_id=1, cookie=100):
361 """Setup multicast forwarding for the mcast address using mcast vlan id
362 and given port list.
363 """
364
365 # setup group first with given port list
366
367 buckets = [
368 ofp.common.bucket(
369 watch_port=ofp.OFPP_ANY,
370 watch_group=ofp.OFPG_ANY,
371 actions=[
372 ofp.action.pop_vlan(),
373 ofp.action.output(port=port)
374 ])
375 for port in port_list
376 ]
377
378 msg = ofp.message.group_add(
379 group_type=ofp.OFPGT_ALL,
380 group_id=group_id,
381 buckets=buckets
382 )
383
384 self.controller.message_send(msg)
385 do_barrier(self.controller)
386 verify_no_errors(self.controller)
387
388 # Then setup flow rule pointing to group
389
390 match = ofp.match()
391 match.oxm_list.append(ofp.oxm.in_port(olt_port))
392 match.oxm_list.append(ofp.oxm.vlan_vid(ofp.OFPVID_PRESENT | mcast_vlan_id))
393 match.oxm_list.append(ofp.oxm.eth_type(0x800))
394 match.oxm_list.append(ofp.oxm.ipv4_dst(self.ip2int(mcast_addr)))
395 request = ofp.message.flow_add(
396 table_id=test_param_get("table", 0),
397 cookie=cookie,
398 match=match,
399 instructions=[
400 ofp.instruction.apply_actions(
401 actions=[ofp.action.group(group_id=group_id)]),
402 ],
403 buffer_id=ofp.OFP_NO_BUFFER, priority=1000)
404 self.controller.message_send(request)
405 do_barrier(self.controller)
406 verify_no_errors(self.controller)
407
408 def removeMcastChannel(self, mcast_addr, mcast_vlan_id, port_list, group_id, cookie):
409 """Remove mumticast forwarding for given mcast address"""
410
411 # Remove flow first
412 match = ofp.match()
413 match.oxm_list.append(ofp.oxm.in_port(olt_port))
414 match.oxm_list.append(ofp.oxm.vlan_vid(ofp.OFPVID_PRESENT | mcast_vlan_id))
415 match.oxm_list.append(ofp.oxm.eth_type(0x800))
416 match.oxm_list.append(ofp.oxm.ipv4_dst(self.ip2int(mcast_addr)))
417 request = ofp.message.flow_delete(
418 table_id=test_param_get("table", 0),
419 cookie=cookie,
420 match=match,
421 instructions=[
422 ofp.instruction.apply_actions(
423 actions=[ofp.action.group(group_id=group_id)]),
424 ],
425 buffer_id=ofp.OFP_NO_BUFFER, priority=1000)
426 self.controller.message_send(request)
427 do_barrier(self.controller)
428 verify_no_errors(self.controller)
429
430 # Then remove the group
431 group_delete = ofp.message.group_delete(group_id=group_id)
432 self.controller.message_send(group_delete)
433 do_barrier(self.controller)
434 verify_no_errors(self.controller)
435
436 def ip2int(self, ip):
437 """Convert a dot-notated string IP address"""
438 digits = [int(d) for d in ip.split('.')]
439 assert len(digits) == 4
440 val = (
441 (digits[0] & 0xff) << 24 |
442 (digits[1] & 0xff) << 16 |
443 (digits[2] & 0xff) << 8 |
444 (digits[3] & 0xff))
445 return val
446
447 def mcastIp2McastMac(self, ip):
448 """ Convert a dot-notated IPv4 multicast address string into an multicast MAC address"""
449 digits = [int(d) for d in ip.split('.')]
450 return '01:00:5e:%02x:%02x:%02x' % (digits[1] & 0x7f, digits[2] & 0xff, digits[3] & 0xff)
451
452 def testMcastFlow(self, mcast_addr, mcast_vlan_id, ports=None, numpkt=1, ip_src="66.77.88.99",
453 expect_to_be_blocked=False):
454 """ Send given number of mcast packets using mcast address and vlan_id
455 and check they arrive to given port(s).
456 """
457
458 # construct mcast packet
459 pktlen = 250
460 pktToSend = simple_udp_packet(
461 eth_dst=self.mcastIp2McastMac(mcast_addr),
462 ip_src=ip_src,
463 ip_dst=mcast_addr,
464 pktlen=pktlen+4,
465 dl_vlan_enable=True,
466 vlan_vid=mcast_vlan_id,
467 vlan_pcp=0)
468
469 if device_type == "pmc":
470 pktToReceive = 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=0,
477 vlan_pcp=0)
478 else:
479 pktToReceive = simple_udp_packet(
480 eth_dst=self.mcastIp2McastMac(mcast_addr),
481 ip_src=ip_src,
482 ip_dst=mcast_addr,
483 pktlen=pktlen)
484
485 # send mcast packet to olt
486 self.dataplane.send(olt_port, str(pktToSend))
487
488 # test that packet is received on each designated port
489 ports = [onu_port] if ports is None else ports
490 for port in ports:
491 if expect_to_be_blocked:
492 verify_no_packet(self, pktToReceive, port)
493 else:
494 verify_packet(self, pktToReceive, port)