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