blob: 3a26c9911f3c8eced83b966d4663d750032e2706 [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):
39 self.log.info('add flow', flow=flow, is_down_stream=is_down_stream)
40 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
49 self.log.info('field-type-eth-type',
50 eth_type=classifier_info['eth_type'])
51 elif field.type == fd.IP_PROTO:
52 classifier_info['ip_proto'] = field.ip_proto
53 self.log.info('field-type-ip-proto',
54 ip_proto=classifier_info['ip_proto'])
55 elif field.type == fd.IN_PORT:
56 classifier_info['in_port'] = field.port
57 self.log.info('field-type-in-port',
58 in_port=classifier_info['in_port'])
59 elif field.type == fd.VLAN_VID:
60 classifier_info['vlan_vid'] = field.vlan_vid & 0xfff
61 self.log.info('field-type-vlan-vid',
62 vlan=classifier_info['vlan_vid'])
63 elif field.type == fd.VLAN_PCP:
64 classifier_info['vlan_pcp'] = field.vlan_pcp
65 self.log.info('field-type-vlan-pcp',
66 pcp=classifier_info['vlan_pcp'])
67 elif field.type == fd.UDP_DST:
68 classifier_info['udp_dst'] = field.udp_dst
69 self.log.info('field-type-udp-dst',
70 udp_dst=classifier_info['udp_dst'])
71 elif field.type == fd.UDP_SRC:
72 classifier_info['udp_src'] = field.udp_src
73 self.log.info('field-type-udp-src',
74 udp_src=classifier_info['udp_src'])
75 elif field.type == fd.IPV4_DST:
76 classifier_info['ipv4_dst'] = field.ipv4_dst
77 self.log.info('field-type-ipv4-dst',
78 ipv4_dst=classifier_info['ipv4_dst'])
79 elif field.type == fd.IPV4_SRC:
80 classifier_info['ipv4_src'] = field.ipv4_src
81 self.log.info('field-type-ipv4-src',
82 ipv4_dst=classifier_info['ipv4_src'])
83 elif field.type == fd.METADATA:
84 classifier_info['metadata'] = field.table_metadata
85 self.log.info('field-type-metadata',
86 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
94 self.log.info('action-type-output',
95 output=action_info['output'],
96 in_port=classifier_info['in_port'])
97 elif action.type == fd.POP_VLAN:
98 action_info['pop_vlan'] = True
99 self.log.info('action-type-pop-vlan', in_port=in_port)
100 elif action.type == fd.PUSH_VLAN:
101 action_info['push_vlan'] = True
102 action_info['tpid'] = action.push.ethertype
103 self.log.info('action-type-push-vlan',
104 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)
113 self.log.info('action-type-set-field',
114 field=_field, in_port=in_port)
115 if _field.type == fd.VLAN_VID:
116 self.log.info('set-field-type-vlan-vid',
117 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:
145 self.log.info('igmp flow add ignored')
146 else:
147 self.log.info("Invalid-Classifier-to-handle", classifier=classifier,
148 action=action)
149 elif 'eth_type' in classifier:
150 if classifier['eth_type'] == 0x888e:
151 self.log.info('eapol flow add')
152 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:
156 self.log.info('Invalid-flow-type-to-handle', classifier=classifier,
157 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
184 self.log.info('add upstream flow', onu_id=onu_id, classifier=uplink_classifier,
185 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)
193 time.sleep(0.1) # FIXME
194
195 self.log.info('add downstream flow', classifier=downlink_classifier,
196 action=downlink_action, gemport_id=gemport_id, flow_id=flow_id)
197
198 flow = openolt_pb2.Flow(
199 onu_id=onu_id, flow_id=flow_id, flow_type="downstream",
200 access_intf_id=intf_id, gemport_id=gemport_id,
201 classifier=self.mk_classifier(downlink_classifier),
202 action=self.mk_action(downlink_action))
203
204 self.stub.FlowAdd(flow)
205 time.sleep(0.1) # FIXME
206
207 def add_dhcp_trap(self, intf_id, onu_id, classifier, action):
208
209 self.log.info('add dhcp trap', classifier=classifier, action=action)
210
211 action.clear()
212 action['trap_to_host'] = True
213 classifier['pkt_tag_type'] = 'single_tag'
214 classifier.pop('vlan_vid', None)
215
216 gemport_id = platform.mk_gemport_id(onu_id)
217 flow_id = platform.mk_flow_id(intf_id, onu_id, DHCP_FLOW_INDEX)
218
219 upstream_flow = openolt_pb2.Flow(
220 onu_id=onu_id, flow_id=flow_id, flow_type="upstream",
221 gemport_id=gemport_id, classifier=self.mk_classifier(classifier),
222 action=self.mk_action(action))
223
224 self.stub.FlowAdd(upstream_flow)
225
226 def add_eapol_flow(self, intf_id, onu_id, uplink_classifier, uplink_action,
227 uplink_eapol_id=EAPOL_FLOW_INDEX,
228 downlink_eapol_id=EAPOL_DOWNLINK_FLOW_INDEX,
229 vlan_id=DEFAULT_MGMT_VLAN):
230
231 self.log.info('add eapol flow', classifier=uplink_classifier, action=uplink_action)
232
233 downlink_classifier = dict(uplink_classifier)
234 downlink_action = dict(uplink_action)
235
236 gemport_id = platform.mk_gemport_id(onu_id)
237 uplink_flow_id = platform.mk_flow_id(intf_id, onu_id, uplink_eapol_id)
238
239 # Add Upstream EAPOL Flow.
240 uplink_classifier['pkt_tag_type'] = 'single_tag'
241 uplink_classifier['vlan_vid'] = vlan_id
242 uplink_action.clear()
243 uplink_action['trap_to_host'] = True
244
245 upstream_flow = openolt_pb2.Flow(
246 onu_id=onu_id, flow_id=uplink_flow_id, flow_type="upstream",
247 gemport_id=gemport_id, classifier=self.mk_classifier(uplink_classifier),
248 action=self.mk_action(uplink_action))
249
250 self.stub.FlowAdd(upstream_flow)
251
252 # Add Downstream EAPOL Flow.
253 downlink_flow_id = platform.mk_flow_id(intf_id, onu_id, downlink_eapol_id)
254 downlink_classifier['pkt_tag_type'] = 'single_tag'
255 downlink_classifier['vlan_vid'] = vlan_id
256
257 downstream_flow = openolt_pb2.Flow(
258 onu_id=onu_id, flow_id=downlink_flow_id, flow_type="downstream",
259 gemport_id=gemport_id, classifier=self.mk_classifier(downlink_classifier),
260 action=self.mk_action(downlink_action))
261
262 self.stub.FlowAdd(downstream_flow)
263
264 def mk_classifier(self, classifier_info):
265
266 classifier = openolt_pb2.Classifier()
267
268 if 'eth_type' in classifier_info:
269 classifier.eth_type = classifier_info['eth_type']
270 if 'ip_proto' in classifier_info:
271 classifier.ip_proto = classifier_info['ip_proto']
272 if 'vlan_vid' in classifier_info:
273 classifier.o_vid = classifier_info['vlan_vid']
274 if 'metadata' in classifier_info:
275 classifier.i_vid = classifier_info['metadata']
276 if 'vlan_pcp' in classifier_info:
277 classifier.o_pbits = classifier_info['vlan_pcp']
278 if 'udp_src' in classifier_info:
279 classifier.src_port = classifier_info['udp_src']
280 if 'udp_dst' in classifier_info:
281 classifier.dst_port = classifier_info['udp_dst']
282 if 'ipv4_dst' in classifier_info:
283 classifier.dst_ip = classifier_info['ipv4_dst']
284 if 'ipv4_src' in classifier_info:
285 classifier.src_ip = classifier_info['ipv4_src']
286 if 'pkt_tag_type' in classifier_info:
287 if classifier_info['pkt_tag_type'] == 'single_tag':
288 classifier.pkt_tag_type = 'single_tag'
289 elif classifier_info['pkt_tag_type'] == 'double_tag':
290 classifier.pkt_tag_type = 'double_tag'
291 elif classifier_info['pkt_tag_type'] == 'untagged':
292 classifier.pkt_tag_type = 'untagged'
293 else:
294 classifier.pkt_tag_type = 'none'
295
296 return classifier
297
298 def mk_action(self, action_info):
299 action = openolt_pb2.Action()
300
301 if 'pop_vlan' in action_info:
302 action.o_vid = action_info['vlan_vid']
303 action.cmd.remove_outer_tag = True
304 elif 'push_vlan' in action_info:
305 action.o_vid = action_info['vlan_vid']
306 action.cmd.add_outer_tag = True
307 elif 'trap_to_host' in action_info:
308 action.cmd.trap_to_host = True
309 else:
310 self.log.info('Invalid-action-field')
311 return
312 return action