blob: 52e968ee0012efb9ce755162180b74560c5fba2d [file] [log] [blame]
Zack Williamse940c7a2019-08-21 14:25:39 -07001/*
2 * Copyright 2019-present Ciena Corporation
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 */
16package model
17
18import (
19 "fmt"
20 "github.com/jhump/protoreflect/dynamic"
21)
22
23type FlowFieldFlag uint64
24
25const (
Zack Williamse940c7a2019-08-21 14:25:39 -070026 // Define bit flags for flow fields to determine what is set and
27 // what is not
28 FLOW_FIELD_UNSUPPORTED_MATCH FlowFieldFlag = 1 << iota
29 FLOW_FIELD_UNSUPPORTED_INSTRUCTION
30 FLOW_FIELD_UNSUPPORTED_ACTION
31 FLOW_FIELD_UNSUPPORTED_SET_FIELD
32 FLOW_FIELD_ID
33 FLOW_FIELD_TABLE_ID
34 FLOW_FIELD_DURATION_SEC
35 FLOW_FIELD_DURATION_NSEC
36 FLOW_FIELD_IDLE_TIMEOUT
37 FLOW_FIELD_HARD_TIMEOUT
38 FLOW_FIELD_PACKET_COUNT
39 FLOW_FIELD_BYTE_COUNT
40 FLOW_FIELD_PRIORITY
41 FLOW_FIELD_COOKIE
42 FLOW_FIELD_IN_PORT
43 FLOW_FIELD_ETH_TYPE
44 FLOW_FIELD_VLAN_ID
45 FLOW_FIELD_IP_PROTO
46 FLOW_FIELD_UDP_SRC
47 FLOW_FIELD_UDP_DST
48 FLOW_FIELD_METADATA
49 FLOW_FIELD_SET_VLAN_ID
50 FLOW_FIELD_POP_VLAN
51 FLOW_FIELD_PUSH_VLAN_ID
52 FLOW_FIELD_OUTPUT
53 FLOW_FIELD_GOTO_TABLE
David Bainbridge09c61672019-08-23 19:07:54 +000054 FLOW_FIELD_WRITE_METADATA
Zack Williamse940c7a2019-08-21 14:25:39 -070055 FLOW_FIELD_CLEAR_ACTIONS
David Bainbridge09c61672019-08-23 19:07:54 +000056 FLOW_FIELD_METER
Zack Williamse940c7a2019-08-21 14:25:39 -070057 FLOW_FIELD_TUNNEL_ID
58 FLOW_FIELD_VLAN_PCP
59
60 FLOW_FIELD_HEADER = FLOW_FIELD_ID | FLOW_FIELD_TABLE_ID |
61 FLOW_FIELD_PRIORITY | FLOW_FIELD_COOKIE
62
63 FLOW_FIELD_STATS = FLOW_FIELD_DURATION_SEC | FLOW_FIELD_DURATION_NSEC |
64 FLOW_FIELD_IDLE_TIMEOUT | FLOW_FIELD_HARD_TIMEOUT |
65 FLOW_FIELD_PACKET_COUNT | FLOW_FIELD_BYTE_COUNT
Andrea Campanella086629f2020-02-21 14:15:43 +010066
67 //ReservedVlan Transparent Vlan (Masked Vlan, VLAN_ANY in ONOS Flows)
68 ReservedTransparentVlan = 4096
Zack Williamse940c7a2019-08-21 14:25:39 -070069)
70
71var (
72 // Provide an array of all flags that can be used for iteration
73 AllFlowFieldFlags = []FlowFieldFlag{
74 FLOW_FIELD_UNSUPPORTED_MATCH,
75 FLOW_FIELD_UNSUPPORTED_INSTRUCTION,
76 FLOW_FIELD_UNSUPPORTED_ACTION,
77 FLOW_FIELD_UNSUPPORTED_SET_FIELD,
78 FLOW_FIELD_ID,
79 FLOW_FIELD_TABLE_ID,
80 FLOW_FIELD_DURATION_SEC,
81 FLOW_FIELD_DURATION_NSEC,
82 FLOW_FIELD_IDLE_TIMEOUT,
83 FLOW_FIELD_HARD_TIMEOUT,
84 FLOW_FIELD_PACKET_COUNT,
85 FLOW_FIELD_BYTE_COUNT,
86 FLOW_FIELD_PRIORITY,
87 FLOW_FIELD_COOKIE,
88 FLOW_FIELD_IN_PORT,
89 FLOW_FIELD_ETH_TYPE,
90 FLOW_FIELD_VLAN_ID,
91 FLOW_FIELD_IP_PROTO,
92 FLOW_FIELD_UDP_SRC,
93 FLOW_FIELD_UDP_DST,
94 FLOW_FIELD_METADATA,
95 FLOW_FIELD_SET_VLAN_ID,
96 FLOW_FIELD_POP_VLAN,
97 FLOW_FIELD_PUSH_VLAN_ID,
98 FLOW_FIELD_OUTPUT,
99 FLOW_FIELD_GOTO_TABLE,
David Bainbridge09c61672019-08-23 19:07:54 +0000100 FLOW_FIELD_WRITE_METADATA,
Zack Williamse940c7a2019-08-21 14:25:39 -0700101 FLOW_FIELD_CLEAR_ACTIONS,
David Bainbridge09c61672019-08-23 19:07:54 +0000102 FLOW_FIELD_METER,
Zack Williamse940c7a2019-08-21 14:25:39 -0700103 FLOW_FIELD_TUNNEL_ID,
104 FLOW_FIELD_VLAN_PCP,
105 }
106)
107
108func (f *FlowFieldFlag) Count() int {
109 var count int
110 var bit uint64 = 1
111 var asUint64 = uint64(*f)
112 for i := 0; i < 64; i += 1 {
113 if asUint64&bit > 0 {
114 count += 1
115 }
116 bit <<= 1
117 }
118 return count
119}
120func (f *FlowFieldFlag) IsSet(flag FlowFieldFlag) bool {
121 return *f&flag > 0
122}
123
124func (f *FlowFieldFlag) Set(flag FlowFieldFlag) {
125 *f |= flag
126}
127
128func (f *FlowFieldFlag) Clear(flag FlowFieldFlag) {
129 var mask = ^(flag)
130 *f &= mask
131}
132
133func (f *FlowFieldFlag) Reset() {
134 *f = 0
135}
136
137func (f FlowFieldFlag) String() string {
138 switch f {
139 case FLOW_FIELD_UNSUPPORTED_MATCH:
140 return "UnsupportedMatch"
141 case FLOW_FIELD_UNSUPPORTED_INSTRUCTION:
142 return "UnsupportedInstruction"
143 case FLOW_FIELD_UNSUPPORTED_ACTION:
144 return "UnsupportedAction"
145 case FLOW_FIELD_UNSUPPORTED_SET_FIELD:
146 return "UnsupportedSetField"
147 case FLOW_FIELD_ID:
148 return "Id"
149 case FLOW_FIELD_TABLE_ID:
150 return "TableId"
151 case FLOW_FIELD_DURATION_SEC:
152 return "DurationSec"
153 case FLOW_FIELD_DURATION_NSEC:
154 return "DurationNsec"
155 case FLOW_FIELD_IDLE_TIMEOUT:
156 return "IdleTimeout"
157 case FLOW_FIELD_HARD_TIMEOUT:
158 return "HardTimeout"
159 case FLOW_FIELD_PACKET_COUNT:
160 return "PacketCount"
161 case FLOW_FIELD_BYTE_COUNT:
162 return "ByteCount"
163 case FLOW_FIELD_PRIORITY:
164 return "Priority"
165 case FLOW_FIELD_COOKIE:
166 return "Cookie"
167 case FLOW_FIELD_IN_PORT:
168 return "InPort"
169 case FLOW_FIELD_ETH_TYPE:
170 return "EthType"
171 case FLOW_FIELD_VLAN_ID:
172 return "VlanId"
173 case FLOW_FIELD_IP_PROTO:
174 return "IpProto"
175 case FLOW_FIELD_UDP_SRC:
176 return "UdpSrc"
177 case FLOW_FIELD_UDP_DST:
178 return "UdpDst"
179 case FLOW_FIELD_METADATA:
180 return "Metadata"
181 case FLOW_FIELD_SET_VLAN_ID:
182 return "SetVlanId"
183 case FLOW_FIELD_POP_VLAN:
184 return "PopVlan"
185 case FLOW_FIELD_PUSH_VLAN_ID:
186 return "PushVlanId"
187 case FLOW_FIELD_OUTPUT:
188 return "Output"
189 case FLOW_FIELD_GOTO_TABLE:
190 return "GotoTable"
David Bainbridge09c61672019-08-23 19:07:54 +0000191 case FLOW_FIELD_WRITE_METADATA:
192 return "WriteMetadata"
Zack Williamse940c7a2019-08-21 14:25:39 -0700193 case FLOW_FIELD_CLEAR_ACTIONS:
194 return "ClearActions"
David Bainbridge09c61672019-08-23 19:07:54 +0000195 case FLOW_FIELD_METER:
196 return "MeterId"
Zack Williamse940c7a2019-08-21 14:25:39 -0700197 case FLOW_FIELD_TUNNEL_ID:
198 return "TunnelId"
199 case FLOW_FIELD_VLAN_PCP:
200 return "VlanPcp"
201 default:
202 return "UnknownFieldFlag"
203 }
204}
205
206/*
207 * This is a partial list of OF match/action values. This list will be
208 * expanded as new fields are needed within VOLTHA
209 *
210 * Strings are used in the output structure so that on output the table
211 * can be "sparsely" populated with "empty" cells as opposed to 0 (zeros)
212 * all over the place.
213 */
214type Flow struct {
215 Id string `json:"id"`
216 TableId uint32 `json:"tableid"`
217 DurationSec uint32 `json:"durationsec"`
218 DurationNsec uint32 `json:"durationnsec"`
219 IdleTimeout uint32 `json:"idletimeout"`
220 HardTimeout uint32 `json:"hardtimeout"`
221 PacketCount uint64 `json:"packetcount"`
222 ByteCount uint64 `json:"bytecount"`
223 Priority uint32 `json:"priority"`
224 Cookie string `json:"cookie"`
225 UnsupportedMatch string `json:"unsupportedmatch,omitempty"`
226 InPort string `json:"inport,omitempty"`
227 EthType string `json:"ethtype,omitempty"`
228 VlanId string `json:"vlanid,omitempty"`
229 IpProto string `json:"ipproto,omitempty"`
230 UdpSrc string `json:"udpsrc,omitempty"`
231 UdpDst string `json:"dstsrc,omitempty"`
232 Metadata string `json:"metadata,omitempty"`
233 UnsupportedInstruction string `json:"unsupportedinstruction,omitempty"`
234 UnsupportedAction string `json:"unsupportedaction,omitempty"`
235 UnsupportedSetField string `json:"unsupportedsetfield,omitempty"`
236 SetVlanId string `json:"setvlanid,omitempty"`
237 PopVlan string `json:"popvlan,omitempty"`
238 PushVlanId string `json:"pushvlanid,omitempty"`
239 Output string `json:"output,omitempty"`
240 GotoTable string `json:"gototable,omitempty"`
David Bainbridge09c61672019-08-23 19:07:54 +0000241 WriteMetadata string `json:"writemetadata,omitempty"`
Zack Williamse940c7a2019-08-21 14:25:39 -0700242 ClearActions string `json:"clear,omitempty"`
David Bainbridge09c61672019-08-23 19:07:54 +0000243 MeterId string `json:"meter,omitempty"`
Zack Williamse940c7a2019-08-21 14:25:39 -0700244 TunnelId string `json:"tunnelid,omitempty"`
245 VlanPcp string `json:"vlanpcp,omitempty"`
246
247 populated FlowFieldFlag
248}
249
250func (f *Flow) Count() int {
251 return f.populated.Count()
252}
253
254func (f *Flow) IsSet(flag FlowFieldFlag) bool {
255 return f.populated.IsSet(flag)
256}
257
258func (f *Flow) Set(flag FlowFieldFlag) {
259 f.populated.Set(flag)
260}
261
262func (f *Flow) Clear(flag FlowFieldFlag) {
263 f.populated.Clear(flag)
264}
265
266func (f *Flow) Reset() {
267 f.populated.Reset()
268}
269
270func (f *Flow) Populated() FlowFieldFlag {
271 return f.populated
272}
273
274func toVlanId(vid uint32) string {
275 if vid == 0 {
276 return "untagged"
277 } else if vid&0x1000 > 0 {
278 return fmt.Sprintf("%d", vid-4096)
279 }
280 return fmt.Sprintf("%d", vid)
281}
282
283func appendInt32(base string, val int32) string {
284 if len(base) > 0 {
285 return fmt.Sprintf("%s,%d", base, val)
286 }
287 return fmt.Sprintf("%d", val)
288}
289
290func appendUint32(base string, val uint32) string {
291 if len(base) > 0 {
292 return fmt.Sprintf("%s,%d", base, val)
293 }
294 return fmt.Sprintf("%d", val)
295}
296
297func (f *Flow) PopulateFrom(val *dynamic.Message) {
298
299 f.Reset()
300 f.Id = fmt.Sprintf("%016x", val.GetFieldByName("id").(uint64))
301 f.TableId = val.GetFieldByName("table_id").(uint32)
302 f.Priority = val.GetFieldByName("priority").(uint32)
303 // mask the lower 8 for the cookie, why?
304 cookie := val.GetFieldByName("cookie").(uint64)
305 if cookie == 0 {
306 f.Cookie = "0"
307 } else {
308 f.Cookie = fmt.Sprintf("~%08x", val.GetFieldByName("cookie").(uint64)&0xffffffff)
309 }
310 f.DurationSec = val.GetFieldByName("duration_sec").(uint32)
311 f.DurationNsec = val.GetFieldByName("duration_nsec").(uint32)
312 f.IdleTimeout = val.GetFieldByName("idle_timeout").(uint32)
313 f.HardTimeout = val.GetFieldByName("hard_timeout").(uint32)
314 f.PacketCount = val.GetFieldByName("packet_count").(uint64)
315 f.ByteCount = val.GetFieldByName("byte_count").(uint64)
316 f.Set(FLOW_FIELD_HEADER | FLOW_FIELD_STATS)
317
318 match := val.GetFieldByName("match").(*dynamic.Message)
319 fields := match.GetFieldByName("oxm_fields")
320 for _, ifield := range fields.([]interface{}) {
321 field := ifield.(*dynamic.Message)
322
323 // Only support OFPXMC_OPENFLOW_BASIC (0x8000)
324 if field.GetFieldByName("oxm_class").(int32) != 0x8000 {
325 continue
326 }
327
328 basic := field.GetFieldByName("ofb_field").(*dynamic.Message)
329 switch basic.GetFieldByName("type").(int32) {
330 case 0: // IN_PORT
331 f.Set(FLOW_FIELD_IN_PORT)
332 f.InPort = fmt.Sprintf("%d", basic.GetFieldByName("port").(uint32))
333 case 2: // METADATA
334 f.Set(FLOW_FIELD_METADATA)
335 f.Metadata = fmt.Sprintf("0x%016x", basic.GetFieldByName("table_metadata").(uint64))
336 case 5: // ETH_TYPE
337 f.Set(FLOW_FIELD_ETH_TYPE)
338 f.EthType = fmt.Sprintf("0x%04x", basic.GetFieldByName("eth_type").(uint32))
339 case 6: // VLAN_ID
340 f.Set(FLOW_FIELD_VLAN_ID)
Andrea Campanella086629f2020-02-21 14:15:43 +0100341 vid := basic.GetFieldByName("vlan_vid").(uint32)
342 mask, errMaskGet := basic.TryGetFieldByName("vlan_vid_mask")
343 if vid == ReservedTransparentVlan && errMaskGet == nil && mask.(uint32) == ReservedTransparentVlan {
344 f.VlanId = "any"
345 } else {
346 f.VlanId = toVlanId(vid)
347 }
Zack Williamse940c7a2019-08-21 14:25:39 -0700348 case 7: // VLAN_PCP
349 f.Set(FLOW_FIELD_VLAN_PCP)
350 f.VlanPcp = fmt.Sprintf("%d", basic.GetFieldByName("vlan_pcp").(uint32))
351 case 10: // IP_PROTO
352 f.Set(FLOW_FIELD_IP_PROTO)
353 f.IpProto = fmt.Sprintf("%d", basic.GetFieldByName("ip_proto").(uint32))
354 case 15: // UDP_SRC
355 f.Set(FLOW_FIELD_UDP_SRC)
356 f.UdpSrc = fmt.Sprintf("%d", basic.GetFieldByName("udp_src").(uint32))
357 case 16: // UDP_DST
358 f.Set(FLOW_FIELD_UDP_DST)
359 f.UdpDst = fmt.Sprintf("%d", basic.GetFieldByName("udp_dst").(uint32))
360 case 38: // TUNNEL_ID
361 f.Set(FLOW_FIELD_TUNNEL_ID)
362 f.TunnelId = fmt.Sprintf("%d", basic.GetFieldByName("tunnel_id").(uint64))
363 default:
364 /*
365 * For unsupported match types put them into an
366 * "Unsupported field so the table/json still
367 * outputs relatively correctly as opposed to
368 * having log messages.
369 */
370 f.Set(FLOW_FIELD_UNSUPPORTED_MATCH)
371 f.UnsupportedMatch = appendInt32(f.UnsupportedMatch, basic.GetFieldByName("type").(int32))
372 }
373 }
374 for _, instruction := range val.GetFieldByName("instructions").([]interface{}) {
375 inst := instruction.(*dynamic.Message)
376 switch inst.GetFieldByName("type").(uint32) {
377 case 1: // GOTO_TABLE
378 f.Set(FLOW_FIELD_GOTO_TABLE)
379 goto_table := inst.GetFieldByName("goto_table").(*dynamic.Message)
380 f.GotoTable = fmt.Sprintf("%d", goto_table.GetFieldByName("table_id").(uint32))
David Bainbridge09c61672019-08-23 19:07:54 +0000381 case 2: // WRITE_METADATA
382 f.Set(FLOW_FIELD_WRITE_METADATA)
383 meta := inst.GetFieldByName("write_metadata").(*dynamic.Message)
384 val := meta.GetFieldByName("metadata").(uint64)
385 mask := meta.GetFieldByName("metadata_mask").(uint64)
386 if mask != 0 {
387 f.WriteMetadata = fmt.Sprintf("0x%016x/0x%016x", val, mask)
388 } else {
389 f.WriteMetadata = fmt.Sprintf("0x%016x", val)
390 }
Zack Williamse940c7a2019-08-21 14:25:39 -0700391 case 4: // APPLY_ACTIONS
392 actions := inst.GetFieldByName("actions").(*dynamic.Message)
393 for _, action := range actions.GetFieldByName("actions").([]interface{}) {
394 a := action.(*dynamic.Message)
395 switch a.GetFieldByName("type").(int32) {
396 case 0: // OUTPUT
397 f.Set(FLOW_FIELD_OUTPUT)
398 output := a.GetFieldByName("output").(*dynamic.Message)
399 out := output.GetFieldByName("port").(uint32)
400 switch out & 0x7fffffff {
401 case 0:
402 f.Output = "INVALID"
403 case 0x7ffffff8:
404 f.Output = "IN_PORT"
405 case 0x7ffffff9:
406 f.Output = "TABLE"
407 case 0x7ffffffa:
408 f.Output = "NORMAL"
409 case 0x7ffffffb:
410 f.Output = "FLOOD"
411 case 0x7ffffffc:
412 f.Output = "ALL"
413 case 0x7ffffffd:
414 f.Output = "CONTROLLER"
415 case 0x7ffffffe:
416 f.Output = "LOCAL"
417 case 0x7fffffff:
418 f.Output = "ANY"
419 default:
420 f.Output = fmt.Sprintf("%d", output.GetFieldByName("port").(uint32))
421 }
422 case 17: // PUSH_VLAN
423 f.Set(FLOW_FIELD_PUSH_VLAN_ID)
424 push := a.GetFieldByName("push").(*dynamic.Message)
425 f.PushVlanId = fmt.Sprintf("0x%x", push.GetFieldByName("ethertype").(uint32))
426 case 18: // POP_VLAN
427 f.Set(FLOW_FIELD_POP_VLAN)
428 f.PopVlan = "yes"
429 case 25: // SET_FIELD
430 set := a.GetFieldByName("set_field").(*dynamic.Message).GetFieldByName("field").(*dynamic.Message)
431
432 // Only support OFPXMC_OPENFLOW_BASIC (0x8000)
433 if set.GetFieldByName("oxm_class").(int32) != 0x8000 {
434 continue
435 }
436 basic := set.GetFieldByName("ofb_field").(*dynamic.Message)
437
438 switch basic.GetFieldByName("type").(int32) {
439 case 6: // VLAN_ID
440 f.Set(FLOW_FIELD_SET_VLAN_ID)
441 f.SetVlanId = toVlanId(basic.GetFieldByName("vlan_vid").(uint32))
442 default: // Unsupported
443 /*
444 * For unsupported match types put them into an
445 * "Unsupported field so the table/json still
446 * outputs relatively correctly as opposed to
447 * having log messages.
448 */
449 f.Set(FLOW_FIELD_UNSUPPORTED_SET_FIELD)
450 f.UnsupportedSetField = appendInt32(f.UnsupportedSetField,
451 basic.GetFieldByName("type").(int32))
452 }
453 default: // Unsupported
454 /*
455 * For unsupported match types put them into an
456 * "Unsupported field so the table/json still
457 * outputs relatively correctly as opposed to
458 * having log messages.
459 */
460 f.Set(FLOW_FIELD_UNSUPPORTED_ACTION)
461 f.UnsupportedAction = appendInt32(f.UnsupportedAction,
462 a.GetFieldByName("type").(int32))
463 }
464 }
465 case 5: // CLEAR_ACTIONS
466 // Following current CLI, just assigning empty list
467 f.Set(FLOW_FIELD_CLEAR_ACTIONS)
468 f.ClearActions = "[]"
David Bainbridge09c61672019-08-23 19:07:54 +0000469 case 6: // METER
470 meter := inst.GetFieldByName("meter").(*dynamic.Message)
471 f.Set(FLOW_FIELD_METER)
472 f.MeterId = fmt.Sprintf("%d", meter.GetFieldByName("meter_id").(uint32))
Zack Williamse940c7a2019-08-21 14:25:39 -0700473 default: // Unsupported
474 /*
475 * For unsupported match types put them into an
476 * "Unsupported field so the table/json still
477 * outputs relatively correctly as opposed to
478 * having log messages.
479 */
480 f.Set(FLOW_FIELD_UNSUPPORTED_INSTRUCTION)
481 f.UnsupportedInstruction = appendUint32(f.UnsupportedInstruction,
482 inst.GetFieldByName("type").(uint32))
483 }
484 }
485}