blob: 4547255bec0d229a95a7470be36e2b95a72028b1 [file] [log] [blame]
khenaidoob9203542018-09-17 22:56:37 -04001#
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#
khenaidoo19d7b632018-10-30 10:49:50 -040016import structlog
khenaidoob9203542018-09-17 22:56:37 -040017
18from adapters.protos import openflow_13_pb2 as ofp
khenaidoo19d7b632018-10-30 10:49:50 -040019from hashlib import md5
khenaidoob9203542018-09-17 22:56:37 -040020
khenaidoo19d7b632018-10-30 10:49:50 -040021log = structlog.get_logger()
22
23# aliases
24ofb_field = ofp.ofp_oxm_ofb_field
25action = ofp.ofp_action
26
27# OFPAT_* shortcuts
khenaidoob9203542018-09-17 22:56:37 -040028OUTPUT = ofp.OFPAT_OUTPUT
khenaidoo19d7b632018-10-30 10:49:50 -040029COPY_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
khenaidoob9203542018-09-17 22:56:37 -040052ETH_TYPE = ofp.OFPXMT_OFB_ETH_TYPE
khenaidoo19d7b632018-10-30 10:49:50 -040053VLAN_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
khenaidoob9203542018-09-17 22:56:37 -040057IP_PROTO = ofp.OFPXMT_OFB_IP_PROTO
khenaidoo19d7b632018-10-30 10:49:50 -040058IPV4_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# ofp_action_* shortcuts
89
90def output(port, max_len=ofp.OFPCML_MAX):
91 return action(
92 type=OUTPUT,
93 output=ofp.ofp_action_output(port=port, max_len=max_len)
94 )
95
96def mpls_ttl(ttl):
97 return action(
98 type=SET_MPLS_TTL,
99 mpls_ttl=ofp.ofp_action_mpls_ttl(mpls_ttl=ttl)
100 )
101
102def push_vlan(eth_type):
103 return action(
104 type=PUSH_VLAN,
105 push=ofp.ofp_action_push(ethertype=eth_type)
106 )
107
108def pop_vlan():
109 return action(
110 type=POP_VLAN
111 )
112
113def pop_mpls(eth_type):
114 return action(
115 type=POP_MPLS,
116 pop_mpls=ofp.ofp_action_pop_mpls(ethertype=eth_type)
117 )
118
119def group(group_id):
120 return action(
121 type=GROUP,
122 group=ofp.ofp_action_group(group_id=group_id)
123 )
124
125def nw_ttl(nw_ttl):
126 return action(
127 type=NW_TTL,
128 nw_ttl=ofp.ofp_action_nw_ttl(nw_ttl=nw_ttl)
129 )
130
131def set_field(field):
132 return action(
133 type=SET_FIELD,
134 set_field=ofp.ofp_action_set_field(
135 field=ofp.ofp_oxm_field(
136 oxm_class=ofp.OFPXMC_OPENFLOW_BASIC,
137 ofb_field=field))
138 )
139
140def experimenter(experimenter, data):
141 return action(
142 type=EXPERIMENTER,
143 experimenter=ofp.ofp_action_experimenter(
144 experimenter=experimenter, data=data)
145 )
146
147# ofb_field generators (incomplete set)
148
149def in_port(_in_port):
150 return ofb_field(type=IN_PORT, port=_in_port)
151
152def in_phy_port(_in_phy_port):
153 return ofb_field(type=IN_PHY_PORT, port=_in_phy_port)
154
155def metadata(_table_metadata):
156 return ofb_field(type=METADATA, table_metadata=_table_metadata)
157
158def eth_dst(_eth_dst):
159 return ofb_field(type=ETH_DST, table_metadata=_eth_dst)
160
161def eth_src(_eth_src):
162 return ofb_field(type=ETH_SRC, table_metadata=_eth_src)
163
164def eth_type(_eth_type):
165 return ofb_field(type=ETH_TYPE, eth_type=_eth_type)
166
167def vlan_vid(_vlan_vid):
168 return ofb_field(type=VLAN_VID, vlan_vid=_vlan_vid)
169
170def vlan_pcp(_vlan_pcp):
171 return ofb_field(type=VLAN_PCP, vlan_pcp=_vlan_pcp)
172
173def ip_dscp(_ip_dscp):
174 return ofb_field(type=IP_DSCP, ip_dscp=_ip_dscp)
175
176def ip_ecn(_ip_ecn):
177 return ofb_field(type=IP_ECN, ip_ecn=_ip_ecn)
178
179def ip_proto(_ip_proto):
180 return ofb_field(type=IP_PROTO, ip_proto=_ip_proto)
181
182def ipv4_src(_ipv4_src):
183 return ofb_field(type=IPV4_SRC, ipv4_src=_ipv4_src)
184
185def ipv4_dst(_ipv4_dst):
186 return ofb_field(type=IPV4_DST, ipv4_dst=_ipv4_dst)
187
188def tcp_src(_tcp_src):
189 return ofb_field(type=TCP_SRC, tcp_src=_tcp_src)
190
191def tcp_dst(_tcp_dst):
192 return ofb_field(type=TCP_DST, tcp_dst=_tcp_dst)
193
194def udp_src(_udp_src):
195 return ofb_field(type=UDP_SRC, udp_src=_udp_src)
196
197def udp_dst(_udp_dst):
198 return ofb_field(type=UDP_DST, udp_dst=_udp_dst)
199
200def sctp_src(_sctp_src):
201 return ofb_field(type=SCTP_SRC, sctp_src=_sctp_src)
202
203def sctp_dst(_sctp_dst):
204 return ofb_field(type=SCTP_DST, sctp_dst=_sctp_dst)
205
206def icmpv4_type(_icmpv4_type):
207 return ofb_field(type=ICMPV4_TYPE, icmpv4_type=_icmpv4_type)
208
209def icmpv4_code(_icmpv4_code):
210 return ofb_field(type=ICMPV4_CODE, icmpv4_code=_icmpv4_code)
211
212def arp_op(_arp_op):
213 return ofb_field(type=ARP_OP, arp_op=_arp_op)
214
215def arp_spa(_arp_spa):
216 return ofb_field(type=ARP_SPA, arp_spa=_arp_spa)
217
218def arp_tpa(_arp_tpa):
219 return ofb_field(type=ARP_TPA, arp_tpa=_arp_tpa)
220
221def arp_sha(_arp_sha):
222 return ofb_field(type=ARP_SHA, arp_sha=_arp_sha)
223
224def arp_tha(_arp_tha):
225 return ofb_field(type=ARP_THA, arp_tha=_arp_tha)
226
227def ipv6_src(_ipv6_src):
228 return ofb_field(type=IPV6_SRC, arp_tha=_ipv6_src)
229
230def ipv6_dst(_ipv6_dst):
231 return ofb_field(type=IPV6_DST, arp_tha=_ipv6_dst)
232
233def ipv6_flabel(_ipv6_flabel):
234 return ofb_field(type=IPV6_FLABEL, arp_tha=_ipv6_flabel)
235
236def ipmpv6_type(_icmpv6_type):
237 return ofb_field(type=ICMPV6_TYPE, arp_tha=_icmpv6_type)
238
239def icmpv6_code(_icmpv6_code):
240 return ofb_field(type=ICMPV6_CODE, arp_tha=_icmpv6_code)
241
242def ipv6_nd_target(_ipv6_nd_target):
243 return ofb_field(type=IPV6_ND_TARGET, arp_tha=_ipv6_nd_target)
244
245def ofb_ipv6_nd_sll(_ofb_ipv6_nd_sll):
246 return ofb_field(type=OFB_IPV6_ND_SLL, arp_tha=_ofb_ipv6_nd_sll)
247
248def ipv6_nd_tll(_ipv6_nd_tll):
249 return ofb_field(type=IPV6_ND_TLL, arp_tha=_ipv6_nd_tll)
250
251def mpls_label(_mpls_label):
252 return ofb_field(type=MPLS_LABEL, arp_tha=_mpls_label)
253
254def mpls_tc(_mpls_tc):
255 return ofb_field(type=MPLS_TC, arp_tha=_mpls_tc)
256
257def mpls_bos(_mpls_bos):
258 return ofb_field(type=MPLS_BOS, arp_tha=_mpls_bos)
259
260def pbb_isid(_pbb_isid):
261 return ofb_field(type=PBB_ISID, arp_tha=_pbb_isid)
262
263def tunnel_id(_tunnel_id):
264 return ofb_field(type=TUNNEL_ID, arp_tha=_tunnel_id)
265
266def ipv6_exthdr(_ipv6_exthdr):
267 return ofb_field(type=IPV6_EXTHDR, arp_tha=_ipv6_exthdr)
268
269
270# frequently used extractors:
271
272def get_actions(flow):
273 """Extract list of ofp_action objects from flow spec object"""
274 assert isinstance(flow, ofp.ofp_flow_stats)
275 # we have the following hard assumptions for now
276 for instruction in flow.instructions:
277 if instruction.type == ofp.OFPIT_APPLY_ACTIONS:
278 return instruction.actions.actions
khenaidoob9203542018-09-17 22:56:37 -0400279
280def get_ofb_fields(flow):
281 assert isinstance(flow, ofp.ofp_flow_stats)
282 assert flow.match.type == ofp.OFPMT_OXM
283 ofb_fields = []
284 for field in flow.match.oxm_fields:
285 assert field.oxm_class == ofp.OFPXMC_OPENFLOW_BASIC
286 ofb_fields.append(field.ofb_field)
287 return ofb_fields
288
khenaidoob9203542018-09-17 22:56:37 -0400289def get_out_port(flow):
290 for action in get_actions(flow):
291 if action.type == OUTPUT:
292 return action.output.port
293 return None
khenaidoo19d7b632018-10-30 10:49:50 -0400294
295def get_in_port(flow):
296 for field in get_ofb_fields(flow):
297 if field.type == IN_PORT:
298 return field.port
299 return None
300
301def get_goto_table_id(flow):
302 for instruction in flow.instructions:
303 if instruction.type == ofp.OFPIT_GOTO_TABLE:
304 return instruction.goto_table.table_id
305 return None
306
307def get_metadata(flow):
308 ''' legacy get method (only want lower 32 bits '''
309 for field in get_ofb_fields(flow):
310 if field.type == METADATA:
311 return field.table_metadata & 0xffffffff
312 return None
313
314def get_metadata_64_bit(flow):
315 for field in get_ofb_fields(flow):
316 if field.type == METADATA:
317 return field.table_metadata
318 return None
319
320
321def get_port_number_from_metadata(flow):
322 """
323 The port number (UNI on ONU) is in the lower 32-bits of metadata and
324 the inner_tag is in the upper 32-bits
325
326 This is set in the ONOS OltPipeline as a metadata field
327 """
328 md = get_metadata_64_bit(flow)
329
330 if md is None:
331 return None
332
333 if md <= 0xffffffff:
334 log.warn('onos-upgrade-suggested',
335 netadata=md,
336 message='Legacy MetaData detected form OltPipeline')
337 return md
338
339 return md & 0xffffffff
340
341
342def get_inner_tag_from_metadata(flow):
343 """
344 The port number (UNI on ONU) is in the lower 32-bits of metadata and
345 the inner_tag is in the upper 32-bits
346
347 This is set in the ONOS OltPipeline as a metadata field
348 """
349 md = get_metadata_64_bit(flow)
350
351 if md is None:
352 return None
353
354 if md <= 0xffffffff:
355 log.warn('onos-upgrade-suggested',
356 netadata=md,
357 message='Legacy MetaData detected form OltPipeline')
358 return md
359
360 return (md >> 32) & 0xffffffff
361
362
363# test and extract next table and group information
364def has_next_table(flow):
365 return get_goto_table_id(flow) is not None
366
367def get_group(flow):
368 for action in get_actions(flow):
369 if action.type == GROUP:
370 return action.group.group_id
371 return None
372
373def has_group(flow):
374 return get_group(flow) is not None
375
376def mk_oxm_fields(match_fields):
377 oxm_fields=[
378 ofp.ofp_oxm_field(
379 oxm_class=ofp.OFPXMC_OPENFLOW_BASIC,
380 ofb_field=field
381 ) for field in match_fields
382 ]
383
384 return oxm_fields
385
386def mk_instructions_from_actions(actions):
387 instructions_action = ofp.ofp_instruction_actions()
388 instructions_action.actions.extend(actions)
389 instruction = ofp.ofp_instruction(type=ofp.OFPIT_APPLY_ACTIONS,
390 actions=instructions_action)
391 return [instruction]
392
393def mk_simple_flow_mod(match_fields, actions, command=ofp.OFPFC_ADD,
394 next_table_id=None, **kw):
395 """
396 Convenience function to generare ofp_flow_mod message with OXM BASIC match
397 composed from the match_fields, and single APPLY_ACTIONS instruction with
398 a list if ofp_action objects.
399 :param match_fields: list(ofp_oxm_ofb_field)
400 :param actions: list(ofp_action)
401 :param command: one of OFPFC_*
402 :param kw: additional keyword-based params to ofp_flow_mod
403 :return: initialized ofp_flow_mod object
404 """
405 instructions = [
406 ofp.ofp_instruction(
407 type=ofp.OFPIT_APPLY_ACTIONS,
408 actions=ofp.ofp_instruction_actions(actions=actions)
409 )
410 ]
411 if next_table_id is not None:
412 instructions.append(ofp.ofp_instruction(
413 type=ofp.OFPIT_GOTO_TABLE,
414 goto_table=ofp.ofp_instruction_goto_table(table_id=next_table_id)
415 ))
416
417 return ofp.ofp_flow_mod(
418 command=command,
419 match=ofp.ofp_match(
420 type=ofp.OFPMT_OXM,
421 oxm_fields=[
422 ofp.ofp_oxm_field(
423 oxm_class=ofp.OFPXMC_OPENFLOW_BASIC,
424 ofb_field=field
425 ) for field in match_fields
426 ]
427 ),
428 instructions=instructions,
429 **kw
430 )
431
432
433def mk_multicast_group_mod(group_id, buckets, command=ofp.OFPGC_ADD):
434 group = ofp.ofp_group_mod(
435 command=command,
436 type=ofp.OFPGT_ALL,
437 group_id=group_id,
438 buckets=buckets
439 )
440 return group
441
442
443def hash_flow_stats(flow):
444 """
445 Return unique 64-bit integer hash for flow covering the following
446 attributes: 'table_id', 'priority', 'flags', 'cookie', 'match', '_instruction_string'
447 """
448 _instruction_string = ""
449 for _instruction in flow.instructions:
450 _instruction_string += _instruction.SerializeToString()
451
452 hex = md5('{},{},{},{},{},{}'.format(
453 flow.table_id,
454 flow.priority,
455 flow.flags,
456 flow.cookie,
457 flow.match.SerializeToString(),
458 _instruction_string
459 )).hexdigest()
460 return int(hex[:16], 16)
461
462
463def flow_stats_entry_from_flow_mod_message(mod):
464 flow = ofp.ofp_flow_stats(
465 table_id=mod.table_id,
466 priority=mod.priority,
467 idle_timeout=mod.idle_timeout,
468 hard_timeout=mod.hard_timeout,
469 flags=mod.flags,
470 cookie=mod.cookie,
471 match=mod.match,
472 instructions=mod.instructions
473 )
474 flow.id = hash_flow_stats(flow)
475 return flow
476
477
478def group_entry_from_group_mod(mod):
479 group = ofp.ofp_group_entry(
480 desc=ofp.ofp_group_desc(
481 type=mod.type,
482 group_id=mod.group_id,
483 buckets=mod.buckets
484 ),
485 stats=ofp.ofp_group_stats(
486 group_id=mod.group_id
487 # TODO do we need to instantiate bucket bins?
488 )
489 )
490 return group
491
492
493def mk_flow_stat(**kw):
494 return flow_stats_entry_from_flow_mod_message(mk_simple_flow_mod(**kw))
495
496
497def mk_group_stat(**kw):
498 return group_entry_from_group_mod(mk_multicast_group_mod(**kw))