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