blob: 456ae066176a3dd5c100747c68995f7e41209838 [file] [log] [blame]
William Kurkian6f436d02019-02-06 16:25:01 -05001#
2# Copyright 2017 the original author or authors.
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15#
16import structlog
17
18from python.protos import openflow_13_pb2 as ofp
19from hashlib import md5
20
21log = structlog.get_logger()
22
23# aliases
24ofb_field = ofp.ofp_oxm_ofb_field
25action = ofp.ofp_action
26
27# OFPAT_* shortcuts
28OUTPUT = ofp.OFPAT_OUTPUT
29COPY_TTL_OUT = ofp.OFPAT_COPY_TTL_OUT
30COPY_TTL_IN = ofp.OFPAT_COPY_TTL_IN
31SET_MPLS_TTL = ofp.OFPAT_SET_MPLS_TTL
32DEC_MPLS_TTL = ofp.OFPAT_DEC_MPLS_TTL
33PUSH_VLAN = ofp.OFPAT_PUSH_VLAN
34POP_VLAN = ofp.OFPAT_POP_VLAN
35PUSH_MPLS = ofp.OFPAT_PUSH_MPLS
36POP_MPLS = ofp.OFPAT_POP_MPLS
37SET_QUEUE = ofp.OFPAT_SET_QUEUE
38GROUP = ofp.OFPAT_GROUP
39SET_NW_TTL = ofp.OFPAT_SET_NW_TTL
40NW_TTL = ofp.OFPAT_DEC_NW_TTL
41SET_FIELD = ofp.OFPAT_SET_FIELD
42PUSH_PBB = ofp.OFPAT_PUSH_PBB
43POP_PBB = ofp.OFPAT_POP_PBB
44EXPERIMENTER = ofp.OFPAT_EXPERIMENTER
45
46# OFPXMT_OFB_* shortcuts (incomplete)
47IN_PORT = ofp.OFPXMT_OFB_IN_PORT
48IN_PHY_PORT = ofp.OFPXMT_OFB_IN_PHY_PORT
49METADATA = ofp.OFPXMT_OFB_METADATA
50ETH_DST = ofp.OFPXMT_OFB_ETH_DST
51ETH_SRC = ofp.OFPXMT_OFB_ETH_SRC
52ETH_TYPE = ofp.OFPXMT_OFB_ETH_TYPE
53VLAN_VID = ofp.OFPXMT_OFB_VLAN_VID
54VLAN_PCP = ofp.OFPXMT_OFB_VLAN_PCP
55IP_DSCP = ofp.OFPXMT_OFB_IP_DSCP
56IP_ECN = ofp.OFPXMT_OFB_IP_ECN
57IP_PROTO = ofp.OFPXMT_OFB_IP_PROTO
58IPV4_SRC = ofp.OFPXMT_OFB_IPV4_SRC
59IPV4_DST = ofp.OFPXMT_OFB_IPV4_DST
60TCP_SRC = ofp.OFPXMT_OFB_TCP_SRC
61TCP_DST = ofp.OFPXMT_OFB_TCP_DST
62UDP_SRC = ofp.OFPXMT_OFB_UDP_SRC
63UDP_DST = ofp.OFPXMT_OFB_UDP_DST
64SCTP_SRC = ofp.OFPXMT_OFB_SCTP_SRC
65SCTP_DST = ofp.OFPXMT_OFB_SCTP_DST
66ICMPV4_TYPE = ofp.OFPXMT_OFB_ICMPV4_TYPE
67ICMPV4_CODE = ofp.OFPXMT_OFB_ICMPV4_CODE
68ARP_OP = ofp.OFPXMT_OFB_ARP_OP
69ARP_SPA = ofp.OFPXMT_OFB_ARP_SPA
70ARP_TPA = ofp.OFPXMT_OFB_ARP_TPA
71ARP_SHA = ofp.OFPXMT_OFB_ARP_SHA
72ARP_THA = ofp.OFPXMT_OFB_ARP_THA
73IPV6_SRC = ofp.OFPXMT_OFB_IPV6_SRC
74IPV6_DST = ofp.OFPXMT_OFB_IPV6_DST
75IPV6_FLABEL = ofp.OFPXMT_OFB_IPV6_FLABEL
76ICMPV6_TYPE = ofp.OFPXMT_OFB_ICMPV6_TYPE
77ICMPV6_CODE = ofp.OFPXMT_OFB_ICMPV6_CODE
78IPV6_ND_TARGET = ofp.OFPXMT_OFB_IPV6_ND_TARGET
79OFB_IPV6_ND_SLL = ofp.OFPXMT_OFB_IPV6_ND_SLL
80IPV6_ND_TLL = ofp.OFPXMT_OFB_IPV6_ND_TLL
81MPLS_LABEL = ofp.OFPXMT_OFB_MPLS_LABEL
82MPLS_TC = ofp.OFPXMT_OFB_MPLS_TC
83MPLS_BOS = ofp.OFPXMT_OFB_MPLS_BOS
84PBB_ISID = ofp.OFPXMT_OFB_PBB_ISID
85TUNNEL_ID = ofp.OFPXMT_OFB_TUNNEL_ID
86IPV6_EXTHDR = ofp.OFPXMT_OFB_IPV6_EXTHDR
87
88
89# ofp_action_* shortcuts
90
91def output(port, max_len=ofp.OFPCML_MAX):
92 return action(
93 type=OUTPUT,
94 output=ofp.ofp_action_output(port=port, max_len=max_len)
95 )
96
97
98def mpls_ttl(ttl):
99 return action(
100 type=SET_MPLS_TTL,
101 mpls_ttl=ofp.ofp_action_mpls_ttl(mpls_ttl=ttl)
102 )
103
104
105def push_vlan(eth_type):
106 return action(
107 type=PUSH_VLAN,
108 push=ofp.ofp_action_push(ethertype=eth_type)
109 )
110
111
112def pop_vlan():
113 return action(
114 type=POP_VLAN
115 )
116
117
118def pop_mpls(eth_type):
119 return action(
120 type=POP_MPLS,
121 pop_mpls=ofp.ofp_action_pop_mpls(ethertype=eth_type)
122 )
123
124
125def group(group_id):
126 return action(
127 type=GROUP,
128 group=ofp.ofp_action_group(group_id=group_id)
129 )
130
131
132def nw_ttl(nw_ttl):
133 return action(
134 type=NW_TTL,
135 nw_ttl=ofp.ofp_action_nw_ttl(nw_ttl=nw_ttl)
136 )
137
138
139def set_field(field):
140 return action(
141 type=SET_FIELD,
142 set_field=ofp.ofp_action_set_field(
143 field=ofp.ofp_oxm_field(
144 oxm_class=ofp.OFPXMC_OPENFLOW_BASIC,
145 ofb_field=field))
146 )
147
148
149def experimenter(experimenter, data):
150 return action(
151 type=EXPERIMENTER,
152 experimenter=ofp.ofp_action_experimenter(
153 experimenter=experimenter, data=data)
154 )
155
156
157# ofb_field generators (incomplete set)
158
159def in_port(_in_port):
160 return ofb_field(type=IN_PORT, port=_in_port)
161
162
163def in_phy_port(_in_phy_port):
164 return ofb_field(type=IN_PHY_PORT, port=_in_phy_port)
165
166
167def metadata(_table_metadata):
168 return ofb_field(type=METADATA, table_metadata=_table_metadata)
169
170
171def eth_dst(_eth_dst):
172 return ofb_field(type=ETH_DST, table_metadata=_eth_dst)
173
174
175def eth_src(_eth_src):
176 return ofb_field(type=ETH_SRC, table_metadata=_eth_src)
177
178
179def eth_type(_eth_type):
180 return ofb_field(type=ETH_TYPE, eth_type=_eth_type)
181
182
183def vlan_vid(_vlan_vid):
184 return ofb_field(type=VLAN_VID, vlan_vid=_vlan_vid)
185
186
187def vlan_pcp(_vlan_pcp):
188 return ofb_field(type=VLAN_PCP, vlan_pcp=_vlan_pcp)
189
190
191def ip_dscp(_ip_dscp):
192 return ofb_field(type=IP_DSCP, ip_dscp=_ip_dscp)
193
194
195def ip_ecn(_ip_ecn):
196 return ofb_field(type=IP_ECN, ip_ecn=_ip_ecn)
197
198
199def ip_proto(_ip_proto):
200 return ofb_field(type=IP_PROTO, ip_proto=_ip_proto)
201
202
203def ipv4_src(_ipv4_src):
204 return ofb_field(type=IPV4_SRC, ipv4_src=_ipv4_src)
205
206
207def ipv4_dst(_ipv4_dst):
208 return ofb_field(type=IPV4_DST, ipv4_dst=_ipv4_dst)
209
210
211def tcp_src(_tcp_src):
212 return ofb_field(type=TCP_SRC, tcp_src=_tcp_src)
213
214
215def tcp_dst(_tcp_dst):
216 return ofb_field(type=TCP_DST, tcp_dst=_tcp_dst)
217
218
219def udp_src(_udp_src):
220 return ofb_field(type=UDP_SRC, udp_src=_udp_src)
221
222
223def udp_dst(_udp_dst):
224 return ofb_field(type=UDP_DST, udp_dst=_udp_dst)
225
226
227def sctp_src(_sctp_src):
228 return ofb_field(type=SCTP_SRC, sctp_src=_sctp_src)
229
230
231def sctp_dst(_sctp_dst):
232 return ofb_field(type=SCTP_DST, sctp_dst=_sctp_dst)
233
234
235def icmpv4_type(_icmpv4_type):
236 return ofb_field(type=ICMPV4_TYPE, icmpv4_type=_icmpv4_type)
237
238
239def icmpv4_code(_icmpv4_code):
240 return ofb_field(type=ICMPV4_CODE, icmpv4_code=_icmpv4_code)
241
242
243def arp_op(_arp_op):
244 return ofb_field(type=ARP_OP, arp_op=_arp_op)
245
246
247def arp_spa(_arp_spa):
248 return ofb_field(type=ARP_SPA, arp_spa=_arp_spa)
249
250
251def arp_tpa(_arp_tpa):
252 return ofb_field(type=ARP_TPA, arp_tpa=_arp_tpa)
253
254
255def arp_sha(_arp_sha):
256 return ofb_field(type=ARP_SHA, arp_sha=_arp_sha)
257
258
259def arp_tha(_arp_tha):
260 return ofb_field(type=ARP_THA, arp_tha=_arp_tha)
261
262
263def ipv6_src(_ipv6_src):
264 return ofb_field(type=IPV6_SRC, arp_tha=_ipv6_src)
265
266
267def ipv6_dst(_ipv6_dst):
268 return ofb_field(type=IPV6_DST, arp_tha=_ipv6_dst)
269
270
271def ipv6_flabel(_ipv6_flabel):
272 return ofb_field(type=IPV6_FLABEL, arp_tha=_ipv6_flabel)
273
274
275def ipmpv6_type(_icmpv6_type):
276 return ofb_field(type=ICMPV6_TYPE, arp_tha=_icmpv6_type)
277
278
279def icmpv6_code(_icmpv6_code):
280 return ofb_field(type=ICMPV6_CODE, arp_tha=_icmpv6_code)
281
282
283def ipv6_nd_target(_ipv6_nd_target):
284 return ofb_field(type=IPV6_ND_TARGET, arp_tha=_ipv6_nd_target)
285
286
287def ofb_ipv6_nd_sll(_ofb_ipv6_nd_sll):
288 return ofb_field(type=OFB_IPV6_ND_SLL, arp_tha=_ofb_ipv6_nd_sll)
289
290
291def ipv6_nd_tll(_ipv6_nd_tll):
292 return ofb_field(type=IPV6_ND_TLL, arp_tha=_ipv6_nd_tll)
293
294
295def mpls_label(_mpls_label):
296 return ofb_field(type=MPLS_LABEL, arp_tha=_mpls_label)
297
298
299def mpls_tc(_mpls_tc):
300 return ofb_field(type=MPLS_TC, arp_tha=_mpls_tc)
301
302
303def mpls_bos(_mpls_bos):
304 return ofb_field(type=MPLS_BOS, arp_tha=_mpls_bos)
305
306
307def pbb_isid(_pbb_isid):
308 return ofb_field(type=PBB_ISID, arp_tha=_pbb_isid)
309
310
311def tunnel_id(_tunnel_id):
312 return ofb_field(type=TUNNEL_ID, arp_tha=_tunnel_id)
313
314
315def ipv6_exthdr(_ipv6_exthdr):
316 return ofb_field(type=IPV6_EXTHDR, arp_tha=_ipv6_exthdr)
317
318
319# frequently used extractors:
320
321def get_actions(flow):
322 """Extract list of ofp_action objects from flow spec object"""
323 assert isinstance(flow, ofp.ofp_flow_stats)
324 # we have the following hard assumptions for now
325 for instruction in flow.instructions:
326 if instruction.type == ofp.OFPIT_APPLY_ACTIONS:
327 return instruction.actions.actions
328
329
330def get_ofb_fields(flow):
331 assert isinstance(flow, ofp.ofp_flow_stats)
332 assert flow.match.type == ofp.OFPMT_OXM
333 ofb_fields = []
334 for field in flow.match.oxm_fields:
335 assert field.oxm_class == ofp.OFPXMC_OPENFLOW_BASIC
336 ofb_fields.append(field.ofb_field)
337 return ofb_fields
338
339
340def get_out_port(flow):
341 for action in get_actions(flow):
342 if action.type == OUTPUT:
343 return action.output.port
344 return None
345
346
347def get_in_port(flow):
348 for field in get_ofb_fields(flow):
349 if field.type == IN_PORT:
350 return field.port
351 return None
352
353
354def get_goto_table_id(flow):
355 for instruction in flow.instructions:
356 if instruction.type == ofp.OFPIT_GOTO_TABLE:
357 return instruction.goto_table.table_id
358 return None
359
360
361def get_metadata(flow):
362 ''' legacy get method (only want lower 32 bits '''
363 for field in get_ofb_fields(flow):
364 if field.type == METADATA:
365 return field.table_metadata & 0xffffffff
366 return None
367
368
369def get_metadata_64_bit(flow):
370 for field in get_ofb_fields(flow):
371 if field.type == METADATA:
372 return field.table_metadata
373 return None
374
375
376def get_port_number_from_metadata(flow):
377 """
378 The port number (UNI on ONU) is in the lower 32-bits of metadata and
379 the inner_tag is in the upper 32-bits
380
381 This is set in the ONOS OltPipeline as a metadata field
382 """
383 md = get_metadata_64_bit(flow)
384
385 if md is None:
386 return None
387
388 if md <= 0xffffffff:
389 log.warn('onos-upgrade-suggested',
390 netadata=md,
391 message='Legacy MetaData detected form OltPipeline')
392 return md
393
394 return md & 0xffffffff
395
396
397def get_inner_tag_from_metadata(flow):
398 """
399 The port number (UNI on ONU) is in the lower 32-bits of metadata and
400 the inner_tag is in the upper 32-bits
401
402 This is set in the ONOS OltPipeline as a metadata field
403 """
404 md = get_metadata_64_bit(flow)
405
406 if md is None:
407 return None
408
409 if md <= 0xffffffff:
410 log.warn('onos-upgrade-suggested',
411 netadata=md,
412 message='Legacy MetaData detected form OltPipeline')
413 return md
414
415 return (md >> 32) & 0xffffffff
416
417
418# test and extract next table and group information
419def has_next_table(flow):
420 return get_goto_table_id(flow) is not None
421
422
423def get_group(flow):
424 for action in get_actions(flow):
425 if action.type == GROUP:
426 return action.group.group_id
427 return None
428
429
430def has_group(flow):
431 return get_group(flow) is not None
432
433
434def mk_oxm_fields(match_fields):
435 oxm_fields = [
436 ofp.ofp_oxm_field(
437 oxm_class=ofp.OFPXMC_OPENFLOW_BASIC,
438 ofb_field=field
439 ) for field in match_fields
440 ]
441
442 return oxm_fields
443
444
445def mk_instructions_from_actions(actions):
446 instructions_action = ofp.ofp_instruction_actions()
447 instructions_action.actions.extend(actions)
448 instruction = ofp.ofp_instruction(type=ofp.OFPIT_APPLY_ACTIONS,
449 actions=instructions_action)
450 return [instruction]
451
452
453def mk_simple_flow_mod(match_fields, actions, command=ofp.OFPFC_ADD,
454 next_table_id=None, **kw):
455 """
456 Convenience function to generare ofp_flow_mod message with OXM BASIC match
457 composed from the match_fields, and single APPLY_ACTIONS instruction with
458 a list if ofp_action objects.
459 :param match_fields: list(ofp_oxm_ofb_field)
460 :param actions: list(ofp_action)
461 :param command: one of OFPFC_*
462 :param kw: additional keyword-based params to ofp_flow_mod
463 :return: initialized ofp_flow_mod object
464 """
465 instructions = [
466 ofp.ofp_instruction(
467 type=ofp.OFPIT_APPLY_ACTIONS,
468 actions=ofp.ofp_instruction_actions(actions=actions)
469 )
470 ]
471 if next_table_id is not None:
472 instructions.append(ofp.ofp_instruction(
473 type=ofp.OFPIT_GOTO_TABLE,
474 goto_table=ofp.ofp_instruction_goto_table(table_id=next_table_id)
475 ))
476
477 return ofp.ofp_flow_mod(
478 command=command,
479 match=ofp.ofp_match(
480 type=ofp.OFPMT_OXM,
481 oxm_fields=[
482 ofp.ofp_oxm_field(
483 oxm_class=ofp.OFPXMC_OPENFLOW_BASIC,
484 ofb_field=field
485 ) for field in match_fields
486 ]
487 ),
488 instructions=instructions,
489 **kw
490 )
491
492
493def mk_multicast_group_mod(group_id, buckets, command=ofp.OFPGC_ADD):
494 group = ofp.ofp_group_mod(
495 command=command,
496 type=ofp.OFPGT_ALL,
497 group_id=group_id,
498 buckets=buckets
499 )
500 return group
501
502
503def hash_flow_stats(flow):
504 """
505 Return unique 64-bit integer hash for flow covering the following
506 attributes: 'table_id', 'priority', 'flags', 'cookie', 'match', '_instruction_string'
507 """
508 _instruction_string = ""
509 for _instruction in flow.instructions:
510 _instruction_string += _instruction.SerializeToString()
511
512 hex = md5('{},{},{},{},{},{}'.format(
513 flow.table_id,
514 flow.priority,
515 flow.flags,
516 flow.cookie,
517 flow.match.SerializeToString(),
518 _instruction_string
519 )).hexdigest()
520 return int(hex[:16], 16)
521
522
523def flow_stats_entry_from_flow_mod_message(mod):
524 flow = ofp.ofp_flow_stats(
525 table_id=mod.table_id,
526 priority=mod.priority,
527 idle_timeout=mod.idle_timeout,
528 hard_timeout=mod.hard_timeout,
529 flags=mod.flags,
530 cookie=mod.cookie,
531 match=mod.match,
532 instructions=mod.instructions
533 )
534 flow.id = hash_flow_stats(flow)
535 return flow
536
537
538def group_entry_from_group_mod(mod):
539 group = ofp.ofp_group_entry(
540 desc=ofp.ofp_group_desc(
541 type=mod.type,
542 group_id=mod.group_id,
543 buckets=mod.buckets
544 ),
545 stats=ofp.ofp_group_stats(
546 group_id=mod.group_id
547 # TODO do we need to instantiate bucket bins?
548 )
549 )
550 return group
551
552
553def mk_flow_stat(**kw):
554 return flow_stats_entry_from_flow_mod_message(mk_simple_flow_mod(**kw))
555
556
557def mk_group_stat(**kw):
558 return group_entry_from_group_mod(mk_multicast_group_mod(**kw))