blob: 2c63df89f5ce510534c0c9fbf361e6dfcfe62d22 [file] [log] [blame]
Shad Ansari2dda4f32018-05-17 07:16:07 +00001#
2# Copyright 2018 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#
16
17import time
18
19from voltha.protos.openflow_13_pb2 import OFPXMC_OPENFLOW_BASIC
20import voltha.core.flow_decomposer as fd
21import openolt_platform as platform
22from voltha.adapters.openolt.protos import openolt_pb2
23
24HSIA_FLOW_INDEX = 0 # FIXME
25DHCP_FLOW_INDEX = 1 # FIXME
26EAPOL_FLOW_INDEX = 2 # FIXME
27EAPOL_DOWNLINK_FLOW_INDEX = 3 # FIXME
28
29# FIXME - see also BRDCM_DEFAULT_VLAN in broadcom_onu.py
30DEFAULT_MGMT_VLAN = 4091
31
32class OpenOltFlowMgr(object):
33
34 def __init__(self, log, stub):
35 self.log = log
36 self.stub = stub
37
38 def add_flow(self, flow, is_down_stream):
Shad Ansarie048aaa2018-05-18 18:27:21 +000039 self.log.debug('add flow', flow=flow, is_down_stream=is_down_stream)
Shad Ansari2dda4f32018-05-17 07:16:07 +000040 classifier_info = dict()
41 action_info = dict()
42
43 in_port = fd.get_in_port(flow)
44 assert in_port is not None
45
46 for field in fd.get_ofb_fields(flow):
47 if field.type == fd.ETH_TYPE:
48 classifier_info['eth_type'] = field.eth_type
Shad Ansarie048aaa2018-05-18 18:27:21 +000049 self.log.debug('field-type-eth-type',
Shad Ansari2dda4f32018-05-17 07:16:07 +000050 eth_type=classifier_info['eth_type'])
51 elif field.type == fd.IP_PROTO:
52 classifier_info['ip_proto'] = field.ip_proto
Shad Ansarie048aaa2018-05-18 18:27:21 +000053 self.log.debug('field-type-ip-proto',
Shad Ansari2dda4f32018-05-17 07:16:07 +000054 ip_proto=classifier_info['ip_proto'])
55 elif field.type == fd.IN_PORT:
56 classifier_info['in_port'] = field.port
Shad Ansarie048aaa2018-05-18 18:27:21 +000057 self.log.debug('field-type-in-port',
Shad Ansari2dda4f32018-05-17 07:16:07 +000058 in_port=classifier_info['in_port'])
59 elif field.type == fd.VLAN_VID:
60 classifier_info['vlan_vid'] = field.vlan_vid & 0xfff
Shad Ansarie048aaa2018-05-18 18:27:21 +000061 self.log.debug('field-type-vlan-vid',
Shad Ansari2dda4f32018-05-17 07:16:07 +000062 vlan=classifier_info['vlan_vid'])
63 elif field.type == fd.VLAN_PCP:
64 classifier_info['vlan_pcp'] = field.vlan_pcp
Shad Ansarie048aaa2018-05-18 18:27:21 +000065 self.log.debug('field-type-vlan-pcp',
Shad Ansari2dda4f32018-05-17 07:16:07 +000066 pcp=classifier_info['vlan_pcp'])
67 elif field.type == fd.UDP_DST:
68 classifier_info['udp_dst'] = field.udp_dst
Shad Ansarie048aaa2018-05-18 18:27:21 +000069 self.log.debug('field-type-udp-dst',
Shad Ansari2dda4f32018-05-17 07:16:07 +000070 udp_dst=classifier_info['udp_dst'])
71 elif field.type == fd.UDP_SRC:
72 classifier_info['udp_src'] = field.udp_src
Shad Ansarie048aaa2018-05-18 18:27:21 +000073 self.log.debug('field-type-udp-src',
Shad Ansari2dda4f32018-05-17 07:16:07 +000074 udp_src=classifier_info['udp_src'])
75 elif field.type == fd.IPV4_DST:
76 classifier_info['ipv4_dst'] = field.ipv4_dst
Shad Ansarie048aaa2018-05-18 18:27:21 +000077 self.log.debug('field-type-ipv4-dst',
Shad Ansari2dda4f32018-05-17 07:16:07 +000078 ipv4_dst=classifier_info['ipv4_dst'])
79 elif field.type == fd.IPV4_SRC:
80 classifier_info['ipv4_src'] = field.ipv4_src
Shad Ansarie048aaa2018-05-18 18:27:21 +000081 self.log.debug('field-type-ipv4-src',
Shad Ansari2dda4f32018-05-17 07:16:07 +000082 ipv4_dst=classifier_info['ipv4_src'])
83 elif field.type == fd.METADATA:
84 classifier_info['metadata'] = field.table_metadata
Shad Ansarie048aaa2018-05-18 18:27:21 +000085 self.log.debug('field-type-metadata',
Shad Ansari2dda4f32018-05-17 07:16:07 +000086 metadata=classifier_info['metadata'])
87 else:
88 raise NotImplementedError('field.type={}'.format(
89 field.type))
90
91 for action in fd.get_actions(flow):
92 if action.type == fd.OUTPUT:
93 action_info['output'] = action.output.port
Shad Ansarie048aaa2018-05-18 18:27:21 +000094 self.log.debug('action-type-output',
Shad Ansari2dda4f32018-05-17 07:16:07 +000095 output=action_info['output'],
96 in_port=classifier_info['in_port'])
97 elif action.type == fd.POP_VLAN:
98 action_info['pop_vlan'] = True
Shad Ansarie048aaa2018-05-18 18:27:21 +000099 self.log.debug('action-type-pop-vlan', in_port=in_port)
Shad Ansari2dda4f32018-05-17 07:16:07 +0000100 elif action.type == fd.PUSH_VLAN:
101 action_info['push_vlan'] = True
102 action_info['tpid'] = action.push.ethertype
Shad Ansarie048aaa2018-05-18 18:27:21 +0000103 self.log.debug('action-type-push-vlan',
Shad Ansari2dda4f32018-05-17 07:16:07 +0000104 push_tpid=action_info['tpid'], in_port=in_port)
105 if action.push.ethertype != 0x8100:
106 self.log.error('unhandled-tpid',
107 ethertype=action.push.ethertype)
108 elif action.type == fd.SET_FIELD:
109 # action_info['action_type'] = 'set_field'
110 _field = action.set_field.field.ofb_field
111 assert (action.set_field.field.oxm_class ==
112 OFPXMC_OPENFLOW_BASIC)
Shad Ansarie048aaa2018-05-18 18:27:21 +0000113 self.log.debug('action-type-set-field',
Shad Ansari2dda4f32018-05-17 07:16:07 +0000114 field=_field, in_port=in_port)
115 if _field.type == fd.VLAN_VID:
Shad Ansarie048aaa2018-05-18 18:27:21 +0000116 self.log.debug('set-field-type-vlan-vid',
Shad Ansari2dda4f32018-05-17 07:16:07 +0000117 vlan_vid=_field.vlan_vid & 0xfff)
118 action_info['vlan_vid'] = (_field.vlan_vid & 0xfff)
119 else:
120 self.log.error('unsupported-action-set-field-type',
121 field_type=_field.type)
122 else:
123 self.log.error('unsupported-action-type',
124 action_type=action.type, in_port=in_port)
125
126 # FIXME - Why ignore downstream flows?
127 if is_down_stream is False:
128 intf_id = platform.intf_id_from_port_num(classifier_info['in_port'])
129 onu_id = platform.onu_id_from_port_num(classifier_info['in_port'])
130 self.divide_and_add_flow(intf_id, onu_id, classifier_info, action_info)
131 #else:
132 # self.log.info('ignore downstream flow', flow=flow,
133 # classifier_info=classifier_info,
134 # action_info=action_info)
135
136 # FIXME - No need for divide_and_add_flow if
137 # both upstream and downstream flows
138 # are acted upon (not just upstream flows).
139 def divide_and_add_flow(self, intf_id, onu_id, classifier, action):
140 if 'ip_proto' in classifier:
141 if classifier['ip_proto'] == 17:
142 self.log.debug('dhcp flow add')
143 self.add_dhcp_trap(intf_id, onu_id, classifier, action)
144 elif classifier['ip_proto'] == 2:
Shad Ansarie048aaa2018-05-18 18:27:21 +0000145 self.log.debug('igmp flow add ignored')
Shad Ansari2dda4f32018-05-17 07:16:07 +0000146 else:
Shad Ansarie048aaa2018-05-18 18:27:21 +0000147 self.log.debug("Invalid-Classifier-to-handle", classifier=classifier,
Shad Ansari2dda4f32018-05-17 07:16:07 +0000148 action=action)
149 elif 'eth_type' in classifier:
150 if classifier['eth_type'] == 0x888e:
Shad Ansarie048aaa2018-05-18 18:27:21 +0000151 self.log.debug('eapol flow add')
Shad Ansari2dda4f32018-05-17 07:16:07 +0000152 self.add_eapol_flow(intf_id, onu_id, classifier, action)
153 elif 'push_vlan' in action:
154 self.add_data_flow(intf_id, onu_id, classifier, action)
155 else:
Shad Ansarie048aaa2018-05-18 18:27:21 +0000156 self.log.debug('Invalid-flow-type-to-handle', classifier=classifier,
Shad Ansari2dda4f32018-05-17 07:16:07 +0000157 action=action)
158
159 def add_data_flow(self, intf_id, onu_id, uplink_classifier, uplink_action):
160
161 downlink_classifier = dict(uplink_classifier)
162 downlink_action = dict(uplink_action)
163
164 uplink_classifier['pkt_tag_type'] = 'single_tag'
165
166 downlink_classifier['pkt_tag_type'] = 'double_tag'
167 downlink_classifier['vlan_vid'] = uplink_action['vlan_vid']
168 downlink_classifier['metadata'] = uplink_classifier['vlan_vid']
169 del downlink_action['push_vlan']
170 downlink_action['pop_vlan'] = True
171
172 # To-Do right now only one GEM port is supported, so below method
173 # will take care of handling all the p bits.
174 # We need to revisit when mulitple gem port per p bits is needed.
175 self.add_hsia_flow(intf_id, onu_id, uplink_classifier, uplink_action,
176 downlink_classifier, downlink_action, HSIA_FLOW_INDEX)
177
178 def add_hsia_flow(self, intf_id, onu_id, uplink_classifier, uplink_action,
179 downlink_classifier, downlink_action, hsia_id):
180
181 gemport_id = platform.mk_gemport_id(onu_id)
182 flow_id = platform.mk_flow_id(intf_id, onu_id, hsia_id)
183
Shad Ansarie048aaa2018-05-18 18:27:21 +0000184 self.log.debug('add upstream flow', onu_id=onu_id, classifier=uplink_classifier,
Shad Ansari2dda4f32018-05-17 07:16:07 +0000185 action=uplink_action, gemport_id=gemport_id, flow_id=flow_id)
186
187 flow = openolt_pb2.Flow(
188 onu_id=onu_id, flow_id=flow_id, flow_type="upstream",
189 gemport_id=gemport_id, classifier=self.mk_classifier(uplink_classifier),
190 action=self.mk_action(uplink_action))
191
192 self.stub.FlowAdd(flow)
Shad Ansari2dda4f32018-05-17 07:16:07 +0000193
Shad Ansarie048aaa2018-05-18 18:27:21 +0000194 self.log.debug('add downstream flow', classifier=downlink_classifier,
Shad Ansari2dda4f32018-05-17 07:16:07 +0000195 action=downlink_action, gemport_id=gemport_id, flow_id=flow_id)
196
197 flow = openolt_pb2.Flow(
198 onu_id=onu_id, flow_id=flow_id, flow_type="downstream",
199 access_intf_id=intf_id, gemport_id=gemport_id,
200 classifier=self.mk_classifier(downlink_classifier),
201 action=self.mk_action(downlink_action))
202
203 self.stub.FlowAdd(flow)
Shad Ansari2dda4f32018-05-17 07:16:07 +0000204
205 def add_dhcp_trap(self, intf_id, onu_id, classifier, action):
206
Shad Ansarie048aaa2018-05-18 18:27:21 +0000207 self.log.debug('add dhcp trap', classifier=classifier, action=action)
Shad Ansari2dda4f32018-05-17 07:16:07 +0000208
209 action.clear()
210 action['trap_to_host'] = True
211 classifier['pkt_tag_type'] = 'single_tag'
212 classifier.pop('vlan_vid', None)
213
214 gemport_id = platform.mk_gemport_id(onu_id)
215 flow_id = platform.mk_flow_id(intf_id, onu_id, DHCP_FLOW_INDEX)
216
217 upstream_flow = openolt_pb2.Flow(
218 onu_id=onu_id, flow_id=flow_id, flow_type="upstream",
219 gemport_id=gemport_id, classifier=self.mk_classifier(classifier),
220 action=self.mk_action(action))
221
222 self.stub.FlowAdd(upstream_flow)
223
224 def add_eapol_flow(self, intf_id, onu_id, uplink_classifier, uplink_action,
225 uplink_eapol_id=EAPOL_FLOW_INDEX,
226 downlink_eapol_id=EAPOL_DOWNLINK_FLOW_INDEX,
227 vlan_id=DEFAULT_MGMT_VLAN):
228
Shad Ansarie048aaa2018-05-18 18:27:21 +0000229 self.log.debug('add eapol flow', classifier=uplink_classifier, action=uplink_action)
Shad Ansari2dda4f32018-05-17 07:16:07 +0000230
231 downlink_classifier = dict(uplink_classifier)
232 downlink_action = dict(uplink_action)
233
234 gemport_id = platform.mk_gemport_id(onu_id)
235 uplink_flow_id = platform.mk_flow_id(intf_id, onu_id, uplink_eapol_id)
236
237 # Add Upstream EAPOL Flow.
238 uplink_classifier['pkt_tag_type'] = 'single_tag'
239 uplink_classifier['vlan_vid'] = vlan_id
240 uplink_action.clear()
241 uplink_action['trap_to_host'] = True
242
243 upstream_flow = openolt_pb2.Flow(
244 onu_id=onu_id, flow_id=uplink_flow_id, flow_type="upstream",
245 gemport_id=gemport_id, classifier=self.mk_classifier(uplink_classifier),
246 action=self.mk_action(uplink_action))
247
248 self.stub.FlowAdd(upstream_flow)
249
250 # Add Downstream EAPOL Flow.
251 downlink_flow_id = platform.mk_flow_id(intf_id, onu_id, downlink_eapol_id)
252 downlink_classifier['pkt_tag_type'] = 'single_tag'
253 downlink_classifier['vlan_vid'] = vlan_id
254
255 downstream_flow = openolt_pb2.Flow(
256 onu_id=onu_id, flow_id=downlink_flow_id, flow_type="downstream",
257 gemport_id=gemport_id, classifier=self.mk_classifier(downlink_classifier),
258 action=self.mk_action(downlink_action))
259
260 self.stub.FlowAdd(downstream_flow)
261
262 def mk_classifier(self, classifier_info):
263
264 classifier = openolt_pb2.Classifier()
265
266 if 'eth_type' in classifier_info:
267 classifier.eth_type = classifier_info['eth_type']
268 if 'ip_proto' in classifier_info:
269 classifier.ip_proto = classifier_info['ip_proto']
270 if 'vlan_vid' in classifier_info:
271 classifier.o_vid = classifier_info['vlan_vid']
272 if 'metadata' in classifier_info:
273 classifier.i_vid = classifier_info['metadata']
274 if 'vlan_pcp' in classifier_info:
275 classifier.o_pbits = classifier_info['vlan_pcp']
276 if 'udp_src' in classifier_info:
277 classifier.src_port = classifier_info['udp_src']
278 if 'udp_dst' in classifier_info:
279 classifier.dst_port = classifier_info['udp_dst']
280 if 'ipv4_dst' in classifier_info:
281 classifier.dst_ip = classifier_info['ipv4_dst']
282 if 'ipv4_src' in classifier_info:
283 classifier.src_ip = classifier_info['ipv4_src']
284 if 'pkt_tag_type' in classifier_info:
285 if classifier_info['pkt_tag_type'] == 'single_tag':
286 classifier.pkt_tag_type = 'single_tag'
287 elif classifier_info['pkt_tag_type'] == 'double_tag':
288 classifier.pkt_tag_type = 'double_tag'
289 elif classifier_info['pkt_tag_type'] == 'untagged':
290 classifier.pkt_tag_type = 'untagged'
291 else:
292 classifier.pkt_tag_type = 'none'
293
294 return classifier
295
296 def mk_action(self, action_info):
297 action = openolt_pb2.Action()
298
299 if 'pop_vlan' in action_info:
300 action.o_vid = action_info['vlan_vid']
301 action.cmd.remove_outer_tag = True
302 elif 'push_vlan' in action_info:
303 action.o_vid = action_info['vlan_vid']
304 action.cmd.add_outer_tag = True
305 elif 'trap_to_host' in action_info:
306 action.cmd.trap_to_host = True
307 else:
308 self.log.info('Invalid-action-field')
309 return
310 return action