]> git.proxmox.com Git - mirror_qemu.git/blame - hw/net/rocker/rocker_of_dpa.c
Merge remote-tracking branch 'remotes/stefanha/tags/net-pull-request' into staging
[mirror_qemu.git] / hw / net / rocker / rocker_of_dpa.c
CommitLineData
dc488f88
SF
1/*
2 * QEMU rocker switch emulation - OF-DPA flow processing support
3 *
4 * Copyright (c) 2014 Scott Feldman <sfeldma@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 */
16
17#include "net/eth.h"
18#include "qemu/iov.h"
19#include "qemu/timer.h"
20#include "qmp-commands.h"
21
22#include "rocker.h"
23#include "rocker_hw.h"
24#include "rocker_fp.h"
25#include "rocker_tlv.h"
26#include "rocker_world.h"
27#include "rocker_desc.h"
28#include "rocker_of_dpa.h"
29
30static const MACAddr zero_mac = { .a = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } };
31static const MACAddr ff_mac = { .a = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } };
32
33typedef struct of_dpa {
34 World *world;
35 GHashTable *flow_tbl;
36 GHashTable *group_tbl;
37 unsigned int flow_tbl_max_size;
38 unsigned int group_tbl_max_size;
39} OfDpa;
40
41/* flow_key stolen mostly from OVS
42 *
43 * Note: fields that compare with network packet header fields
44 * are stored in network order (BE) to avoid per-packet field
45 * byte-swaps.
46 */
47
48typedef struct of_dpa_flow_key {
49 uint32_t in_pport; /* ingress port */
50 uint32_t tunnel_id; /* overlay tunnel id */
51 uint32_t tbl_id; /* table id */
52 struct {
53 __be16 vlan_id; /* 0 if no VLAN */
54 MACAddr src; /* ethernet source address */
55 MACAddr dst; /* ethernet destination address */
56 __be16 type; /* ethernet frame type */
57 } eth;
58 struct {
59 uint8_t proto; /* IP protocol or ARP opcode */
60 uint8_t tos; /* IP ToS */
61 uint8_t ttl; /* IP TTL/hop limit */
62 uint8_t frag; /* one of FRAG_TYPE_* */
63 } ip;
64 union {
65 struct {
66 struct {
67 __be32 src; /* IP source address */
68 __be32 dst; /* IP destination address */
69 } addr;
70 union {
71 struct {
72 __be16 src; /* TCP/UDP/SCTP source port */
73 __be16 dst; /* TCP/UDP/SCTP destination port */
74 __be16 flags; /* TCP flags */
75 } tp;
76 struct {
77 MACAddr sha; /* ARP source hardware address */
78 MACAddr tha; /* ARP target hardware address */
79 } arp;
80 };
81 } ipv4;
82 struct {
83 struct {
84 Ipv6Addr src; /* IPv6 source address */
85 Ipv6Addr dst; /* IPv6 destination address */
86 } addr;
87 __be32 label; /* IPv6 flow label */
88 struct {
89 __be16 src; /* TCP/UDP/SCTP source port */
90 __be16 dst; /* TCP/UDP/SCTP destination port */
91 __be16 flags; /* TCP flags */
92 } tp;
93 struct {
94 Ipv6Addr target; /* ND target address */
95 MACAddr sll; /* ND source link layer address */
96 MACAddr tll; /* ND target link layer address */
97 } nd;
98 } ipv6;
99 };
100 int width; /* how many uint64_t's in key? */
101} OfDpaFlowKey;
102
103/* Width of key which includes field 'f' in u64s, rounded up */
104#define FLOW_KEY_WIDTH(f) \
105 ((offsetof(OfDpaFlowKey, f) + \
106 sizeof(((OfDpaFlowKey *)0)->f) + \
107 sizeof(uint64_t) - 1) / sizeof(uint64_t))
108
109typedef struct of_dpa_flow_action {
110 uint32_t goto_tbl;
111 struct {
112 uint32_t group_id;
113 uint32_t tun_log_lport;
114 __be16 vlan_id;
115 } write;
116 struct {
117 __be16 new_vlan_id;
118 uint32_t out_pport;
119 uint8_t copy_to_cpu;
120 __be16 vlan_id;
121 } apply;
122} OfDpaFlowAction;
123
124typedef struct of_dpa_flow {
125 uint32_t lpm;
126 uint32_t priority;
127 uint32_t hardtime;
128 uint32_t idletime;
129 uint64_t cookie;
130 OfDpaFlowKey key;
131 OfDpaFlowKey mask;
132 OfDpaFlowAction action;
133 struct {
134 uint64_t hits;
135 int64_t install_time;
136 int64_t refresh_time;
137 uint64_t rx_pkts;
138 uint64_t tx_pkts;
139 } stats;
140} OfDpaFlow;
141
142typedef struct of_dpa_flow_pkt_fields {
143 uint32_t tunnel_id;
144 struct eth_header *ethhdr;
145 __be16 *h_proto;
146 struct vlan_header *vlanhdr;
147 struct ip_header *ipv4hdr;
148 struct ip6_header *ipv6hdr;
149 Ipv6Addr *ipv6_src_addr;
150 Ipv6Addr *ipv6_dst_addr;
151} OfDpaFlowPktFields;
152
153typedef struct of_dpa_flow_context {
154 uint32_t in_pport;
155 uint32_t tunnel_id;
156 struct iovec *iov;
157 int iovcnt;
158 struct eth_header ethhdr_rewrite;
159 struct vlan_header vlanhdr_rewrite;
160 struct vlan_header vlanhdr;
161 OfDpa *of_dpa;
162 OfDpaFlowPktFields fields;
163 OfDpaFlowAction action_set;
164} OfDpaFlowContext;
165
166typedef struct of_dpa_flow_match {
167 OfDpaFlowKey value;
168 OfDpaFlow *best;
169} OfDpaFlowMatch;
170
171typedef struct of_dpa_group {
172 uint32_t id;
173 union {
174 struct {
175 uint32_t out_pport;
176 uint8_t pop_vlan;
177 } l2_interface;
178 struct {
179 uint32_t group_id;
180 MACAddr src_mac;
181 MACAddr dst_mac;
182 __be16 vlan_id;
183 } l2_rewrite;
184 struct {
185 uint16_t group_count;
186 uint32_t *group_ids;
187 } l2_flood;
188 struct {
189 uint32_t group_id;
190 MACAddr src_mac;
191 MACAddr dst_mac;
192 __be16 vlan_id;
193 uint8_t ttl_check;
194 } l3_unicast;
195 };
196} OfDpaGroup;
197
198static int of_dpa_mask2prefix(__be32 mask)
199{
200 int i;
201 int count = 32;
202
203 for (i = 0; i < 32; i++) {
204 if (!(ntohl(mask) & ((2 << i) - 1))) {
205 count--;
206 }
207 }
208
209 return count;
210}
211
212#if defined(DEBUG_ROCKER)
213static void of_dpa_flow_key_dump(OfDpaFlowKey *key, OfDpaFlowKey *mask)
214{
215 char buf[512], *b = buf, *mac;
216
217 b += sprintf(b, " tbl %2d", key->tbl_id);
218
219 if (key->in_pport || (mask && mask->in_pport)) {
220 b += sprintf(b, " in_pport %2d", key->in_pport);
221 if (mask && mask->in_pport != 0xffffffff) {
222 b += sprintf(b, "/0x%08x", key->in_pport);
223 }
224 }
225
226 if (key->tunnel_id || (mask && mask->tunnel_id)) {
227 b += sprintf(b, " tun %8d", key->tunnel_id);
228 if (mask && mask->tunnel_id != 0xffffffff) {
229 b += sprintf(b, "/0x%08x", key->tunnel_id);
230 }
231 }
232
233 if (key->eth.vlan_id || (mask && mask->eth.vlan_id)) {
234 b += sprintf(b, " vlan %4d", ntohs(key->eth.vlan_id));
235 if (mask && mask->eth.vlan_id != 0xffff) {
236 b += sprintf(b, "/0x%04x", ntohs(key->eth.vlan_id));
237 }
238 }
239
240 if (memcmp(key->eth.src.a, zero_mac.a, ETH_ALEN) ||
241 (mask && memcmp(mask->eth.src.a, zero_mac.a, ETH_ALEN))) {
242 mac = qemu_mac_strdup_printf(key->eth.src.a);
243 b += sprintf(b, " src %s", mac);
244 g_free(mac);
245 if (mask && memcmp(mask->eth.src.a, ff_mac.a, ETH_ALEN)) {
246 mac = qemu_mac_strdup_printf(mask->eth.src.a);
247 b += sprintf(b, "/%s", mac);
248 g_free(mac);
249 }
250 }
251
252 if (memcmp(key->eth.dst.a, zero_mac.a, ETH_ALEN) ||
253 (mask && memcmp(mask->eth.dst.a, zero_mac.a, ETH_ALEN))) {
254 mac = qemu_mac_strdup_printf(key->eth.dst.a);
255 b += sprintf(b, " dst %s", mac);
256 g_free(mac);
257 if (mask && memcmp(mask->eth.dst.a, ff_mac.a, ETH_ALEN)) {
258 mac = qemu_mac_strdup_printf(mask->eth.dst.a);
259 b += sprintf(b, "/%s", mac);
260 g_free(mac);
261 }
262 }
263
264 if (key->eth.type || (mask && mask->eth.type)) {
265 b += sprintf(b, " type 0x%04x", ntohs(key->eth.type));
266 if (mask && mask->eth.type != 0xffff) {
267 b += sprintf(b, "/0x%04x", ntohs(mask->eth.type));
268 }
269 switch (ntohs(key->eth.type)) {
270 case 0x0800:
271 case 0x86dd:
272 if (key->ip.proto || (mask && mask->ip.proto)) {
273 b += sprintf(b, " ip proto %2d", key->ip.proto);
274 if (mask && mask->ip.proto != 0xff) {
275 b += sprintf(b, "/0x%02x", mask->ip.proto);
276 }
277 }
278 if (key->ip.tos || (mask && mask->ip.tos)) {
279 b += sprintf(b, " ip tos %2d", key->ip.tos);
280 if (mask && mask->ip.tos != 0xff) {
281 b += sprintf(b, "/0x%02x", mask->ip.tos);
282 }
283 }
284 break;
285 }
286 switch (ntohs(key->eth.type)) {
287 case 0x0800:
288 if (key->ipv4.addr.dst || (mask && mask->ipv4.addr.dst)) {
289 b += sprintf(b, " dst %s",
290 inet_ntoa(*(struct in_addr *)&key->ipv4.addr.dst));
291 if (mask) {
292 b += sprintf(b, "/%d",
293 of_dpa_mask2prefix(mask->ipv4.addr.dst));
294 }
295 }
296 break;
297 }
298 }
299
300 DPRINTF("%s\n", buf);
301}
302#else
303#define of_dpa_flow_key_dump(k, m)
304#endif
305
306static void _of_dpa_flow_match(void *key, void *value, void *user_data)
307{
308 OfDpaFlow *flow = value;
309 OfDpaFlowMatch *match = user_data;
310 uint64_t *k = (uint64_t *)&flow->key;
311 uint64_t *m = (uint64_t *)&flow->mask;
312 uint64_t *v = (uint64_t *)&match->value;
313 int i;
314
315 if (flow->key.tbl_id == match->value.tbl_id) {
316 of_dpa_flow_key_dump(&flow->key, &flow->mask);
317 }
318
319 if (flow->key.width > match->value.width) {
320 return;
321 }
322
323 for (i = 0; i < flow->key.width; i++, k++, m++, v++) {
324 if ((~*k & *m & *v) | (*k & *m & ~*v)) {
325 return;
326 }
327 }
328
329 DPRINTF("match\n");
330
331 if (!match->best ||
332 flow->priority > match->best->priority ||
333 flow->lpm > match->best->lpm) {
334 match->best = flow;
335 }
336}
337
338static OfDpaFlow *of_dpa_flow_match(OfDpa *of_dpa, OfDpaFlowMatch *match)
339{
340 DPRINTF("\nnew search\n");
341 of_dpa_flow_key_dump(&match->value, NULL);
342
343 g_hash_table_foreach(of_dpa->flow_tbl, _of_dpa_flow_match, match);
344
345 return match->best;
346}
347
348static OfDpaFlow *of_dpa_flow_find(OfDpa *of_dpa, uint64_t cookie)
349{
350 return g_hash_table_lookup(of_dpa->flow_tbl, &cookie);
351}
352
353static int of_dpa_flow_add(OfDpa *of_dpa, OfDpaFlow *flow)
354{
355 g_hash_table_insert(of_dpa->flow_tbl, &flow->cookie, flow);
356
357 return ROCKER_OK;
358}
359
360static void of_dpa_flow_del(OfDpa *of_dpa, OfDpaFlow *flow)
361{
362 g_hash_table_remove(of_dpa->flow_tbl, &flow->cookie);
363}
364
365static OfDpaFlow *of_dpa_flow_alloc(uint64_t cookie)
366{
367 OfDpaFlow *flow;
368 int64_t now = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) / 1000;
369
370 flow = g_malloc0(sizeof(OfDpaFlow));
371 if (!flow) {
372 return NULL;
373 }
374
375 flow->cookie = cookie;
376 flow->mask.tbl_id = 0xffffffff;
377
378 flow->stats.install_time = flow->stats.refresh_time = now;
379
380 return flow;
381}
382
383static void of_dpa_flow_pkt_hdr_reset(OfDpaFlowContext *fc)
384{
385 OfDpaFlowPktFields *fields = &fc->fields;
386
387 fc->iov[0].iov_base = fields->ethhdr;
388 fc->iov[0].iov_len = sizeof(struct eth_header);
389 fc->iov[1].iov_base = fields->vlanhdr;
390 fc->iov[1].iov_len = fields->vlanhdr ? sizeof(struct vlan_header) : 0;
391}
392
393static void of_dpa_flow_pkt_parse(OfDpaFlowContext *fc,
394 const struct iovec *iov, int iovcnt)
395{
396 OfDpaFlowPktFields *fields = &fc->fields;
397 size_t sofar = 0;
398 int i;
399
400 sofar += sizeof(struct eth_header);
401 if (iov->iov_len < sofar) {
402 DPRINTF("flow_pkt_parse underrun on eth_header\n");
403 return;
404 }
405
406 fields->ethhdr = iov->iov_base;
407 fields->h_proto = &fields->ethhdr->h_proto;
408
409 if (ntohs(*fields->h_proto) == ETH_P_VLAN) {
410 sofar += sizeof(struct vlan_header);
411 if (iov->iov_len < sofar) {
412 DPRINTF("flow_pkt_parse underrun on vlan_header\n");
413 return;
414 }
415 fields->vlanhdr = (struct vlan_header *)(fields->ethhdr + 1);
416 fields->h_proto = &fields->vlanhdr->h_proto;
417 }
418
419 switch (ntohs(*fields->h_proto)) {
420 case ETH_P_IP:
421 sofar += sizeof(struct ip_header);
422 if (iov->iov_len < sofar) {
423 DPRINTF("flow_pkt_parse underrun on ip_header\n");
424 return;
425 }
426 fields->ipv4hdr = (struct ip_header *)(fields->h_proto + 1);
427 break;
428 case ETH_P_IPV6:
429 sofar += sizeof(struct ip6_header);
430 if (iov->iov_len < sofar) {
431 DPRINTF("flow_pkt_parse underrun on ip6_header\n");
432 return;
433 }
434 fields->ipv6hdr = (struct ip6_header *)(fields->h_proto + 1);
435 break;
436 }
437
438 /* To facilitate (potential) VLAN tag insertion, Make a
439 * copy of the iov and insert two new vectors at the
440 * beginning for eth hdr and vlan hdr. No data is copied,
441 * just the vectors.
442 */
443
444 of_dpa_flow_pkt_hdr_reset(fc);
445
446 fc->iov[2].iov_base = fields->h_proto + 1;
447 fc->iov[2].iov_len = iov->iov_len - fc->iov[0].iov_len - fc->iov[1].iov_len;
448
449 for (i = 1; i < iovcnt; i++) {
450 fc->iov[i+2] = iov[i];
451 }
452
453 fc->iovcnt = iovcnt + 2;
454}
455
456static void of_dpa_flow_pkt_insert_vlan(OfDpaFlowContext *fc, __be16 vlan_id)
457{
458 OfDpaFlowPktFields *fields = &fc->fields;
459 uint16_t h_proto = fields->ethhdr->h_proto;
460
461 if (fields->vlanhdr) {
462 DPRINTF("flow_pkt_insert_vlan packet already has vlan\n");
463 return;
464 }
465
466 fields->ethhdr->h_proto = htons(ETH_P_VLAN);
467 fields->vlanhdr = &fc->vlanhdr;
468 fields->vlanhdr->h_tci = vlan_id;
469 fields->vlanhdr->h_proto = h_proto;
470 fields->h_proto = &fields->vlanhdr->h_proto;
471
472 fc->iov[1].iov_base = fields->vlanhdr;
473 fc->iov[1].iov_len = sizeof(struct vlan_header);
474}
475
476static void of_dpa_flow_pkt_strip_vlan(OfDpaFlowContext *fc)
477{
478 OfDpaFlowPktFields *fields = &fc->fields;
479
480 if (!fields->vlanhdr) {
481 return;
482 }
483
484 fc->iov[0].iov_len -= sizeof(fields->ethhdr->h_proto);
485 fc->iov[1].iov_base = fields->h_proto;
486 fc->iov[1].iov_len = sizeof(fields->ethhdr->h_proto);
487}
488
489static void of_dpa_flow_pkt_hdr_rewrite(OfDpaFlowContext *fc,
490 uint8_t *src_mac, uint8_t *dst_mac,
491 __be16 vlan_id)
492{
493 OfDpaFlowPktFields *fields = &fc->fields;
494
495 if (src_mac || dst_mac) {
496 memcpy(&fc->ethhdr_rewrite, fields->ethhdr, sizeof(struct eth_header));
497 if (src_mac && memcmp(src_mac, zero_mac.a, ETH_ALEN)) {
498 memcpy(fc->ethhdr_rewrite.h_source, src_mac, ETH_ALEN);
499 }
500 if (dst_mac && memcmp(dst_mac, zero_mac.a, ETH_ALEN)) {
501 memcpy(fc->ethhdr_rewrite.h_dest, dst_mac, ETH_ALEN);
502 }
503 fc->iov[0].iov_base = &fc->ethhdr_rewrite;
504 }
505
506 if (vlan_id && fields->vlanhdr) {
507 fc->vlanhdr_rewrite = fc->vlanhdr;
508 fc->vlanhdr_rewrite.h_tci = vlan_id;
509 fc->iov[1].iov_base = &fc->vlanhdr_rewrite;
510 }
511}
512
513static void of_dpa_flow_ig_tbl(OfDpaFlowContext *fc, uint32_t tbl_id);
514
515static void of_dpa_ig_port_build_match(OfDpaFlowContext *fc,
516 OfDpaFlowMatch *match)
517{
518 match->value.tbl_id = ROCKER_OF_DPA_TABLE_ID_INGRESS_PORT;
519 match->value.in_pport = fc->in_pport;
520 match->value.width = FLOW_KEY_WIDTH(tbl_id);
521}
522
523static void of_dpa_ig_port_miss(OfDpaFlowContext *fc)
524{
525 uint32_t port;
526
527 /* The default on miss is for packets from physical ports
528 * to go to the VLAN Flow Table. There is no default rule
529 * for packets from logical ports, which are dropped on miss.
530 */
531
532 if (fp_port_from_pport(fc->in_pport, &port)) {
533 of_dpa_flow_ig_tbl(fc, ROCKER_OF_DPA_TABLE_ID_VLAN);
534 }
535}
536
537static void of_dpa_vlan_build_match(OfDpaFlowContext *fc,
538 OfDpaFlowMatch *match)
539{
540 match->value.tbl_id = ROCKER_OF_DPA_TABLE_ID_VLAN;
541 match->value.in_pport = fc->in_pport;
542 if (fc->fields.vlanhdr) {
543 match->value.eth.vlan_id = fc->fields.vlanhdr->h_tci;
544 }
545 match->value.width = FLOW_KEY_WIDTH(eth.vlan_id);
546}
547
548static void of_dpa_vlan_insert(OfDpaFlowContext *fc,
549 OfDpaFlow *flow)
550{
551 if (flow->action.apply.new_vlan_id) {
552 of_dpa_flow_pkt_insert_vlan(fc, flow->action.apply.new_vlan_id);
553 }
554}
555
556static void of_dpa_term_mac_build_match(OfDpaFlowContext *fc,
557 OfDpaFlowMatch *match)
558{
559 match->value.tbl_id = ROCKER_OF_DPA_TABLE_ID_TERMINATION_MAC;
560 match->value.in_pport = fc->in_pport;
561 match->value.eth.type = *fc->fields.h_proto;
562 match->value.eth.vlan_id = fc->fields.vlanhdr->h_tci;
563 memcpy(match->value.eth.dst.a, fc->fields.ethhdr->h_dest,
564 sizeof(match->value.eth.dst.a));
565 match->value.width = FLOW_KEY_WIDTH(eth.type);
566}
567
568static void of_dpa_term_mac_miss(OfDpaFlowContext *fc)
569{
570 of_dpa_flow_ig_tbl(fc, ROCKER_OF_DPA_TABLE_ID_BRIDGING);
571}
572
573static void of_dpa_apply_actions(OfDpaFlowContext *fc,
574 OfDpaFlow *flow)
575{
576 fc->action_set.apply.copy_to_cpu = flow->action.apply.copy_to_cpu;
577 fc->action_set.apply.vlan_id = flow->key.eth.vlan_id;
578}
579
580static void of_dpa_bridging_build_match(OfDpaFlowContext *fc,
581 OfDpaFlowMatch *match)
582{
583 match->value.tbl_id = ROCKER_OF_DPA_TABLE_ID_BRIDGING;
584 if (fc->fields.vlanhdr) {
585 match->value.eth.vlan_id = fc->fields.vlanhdr->h_tci;
586 } else if (fc->tunnel_id) {
587 match->value.tunnel_id = fc->tunnel_id;
588 }
589 memcpy(match->value.eth.dst.a, fc->fields.ethhdr->h_dest,
590 sizeof(match->value.eth.dst.a));
591 match->value.width = FLOW_KEY_WIDTH(eth.dst);
592}
593
594static void of_dpa_bridging_learn(OfDpaFlowContext *fc,
595 OfDpaFlow *dst_flow)
596{
597 OfDpaFlowMatch match = { { 0, }, };
598 OfDpaFlow *flow;
599 uint8_t *addr;
600 uint16_t vlan_id;
601 int64_t now = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) / 1000;
602 int64_t refresh_delay = 1;
603
604 /* Do a lookup in bridge table by src_mac/vlan */
605
606 addr = fc->fields.ethhdr->h_source;
607 vlan_id = fc->fields.vlanhdr->h_tci;
608
609 match.value.tbl_id = ROCKER_OF_DPA_TABLE_ID_BRIDGING;
610 match.value.eth.vlan_id = vlan_id;
611 memcpy(match.value.eth.dst.a, addr, sizeof(match.value.eth.dst.a));
612 match.value.width = FLOW_KEY_WIDTH(eth.dst);
613
614 flow = of_dpa_flow_match(fc->of_dpa, &match);
615 if (flow) {
616 if (!memcmp(flow->mask.eth.dst.a, ff_mac.a,
617 sizeof(flow->mask.eth.dst.a))) {
618 /* src_mac/vlan already learned; if in_port and out_port
619 * don't match, the end station has moved and the port
620 * needs updating */
621 /* XXX implement the in_port/out_port check */
622 if (now - flow->stats.refresh_time < refresh_delay) {
623 return;
624 }
625 flow->stats.refresh_time = now;
626 }
627 }
628
629 /* Let driver know about mac/vlan. This may be a new mac/vlan
630 * or a refresh of existing mac/vlan that's been hit after the
631 * refresh_delay.
632 */
633
634 rocker_event_mac_vlan_seen(world_rocker(fc->of_dpa->world),
635 fc->in_pport, addr, vlan_id);
636}
637
638static void of_dpa_bridging_miss(OfDpaFlowContext *fc)
639{
640 of_dpa_bridging_learn(fc, NULL);
641 of_dpa_flow_ig_tbl(fc, ROCKER_OF_DPA_TABLE_ID_ACL_POLICY);
642}
643
644static void of_dpa_bridging_action_write(OfDpaFlowContext *fc,
645 OfDpaFlow *flow)
646{
647 if (flow->action.write.group_id != ROCKER_GROUP_NONE) {
648 fc->action_set.write.group_id = flow->action.write.group_id;
649 }
650 fc->action_set.write.tun_log_lport = flow->action.write.tun_log_lport;
651}
652
653static void of_dpa_unicast_routing_build_match(OfDpaFlowContext *fc,
654 OfDpaFlowMatch *match)
655{
656 match->value.tbl_id = ROCKER_OF_DPA_TABLE_ID_UNICAST_ROUTING;
657 match->value.eth.type = *fc->fields.h_proto;
658 if (fc->fields.ipv4hdr) {
659 match->value.ipv4.addr.dst = fc->fields.ipv4hdr->ip_dst;
660 }
661 if (fc->fields.ipv6_dst_addr) {
662 memcpy(&match->value.ipv6.addr.dst, fc->fields.ipv6_dst_addr,
663 sizeof(match->value.ipv6.addr.dst));
664 }
665 match->value.width = FLOW_KEY_WIDTH(ipv6.addr.dst);
666}
667
668static void of_dpa_unicast_routing_miss(OfDpaFlowContext *fc)
669{
670 of_dpa_flow_ig_tbl(fc, ROCKER_OF_DPA_TABLE_ID_ACL_POLICY);
671}
672
673static void of_dpa_unicast_routing_action_write(OfDpaFlowContext *fc,
674 OfDpaFlow *flow)
675{
676 if (flow->action.write.group_id != ROCKER_GROUP_NONE) {
677 fc->action_set.write.group_id = flow->action.write.group_id;
678 }
679}
680
681static void
682of_dpa_multicast_routing_build_match(OfDpaFlowContext *fc,
683 OfDpaFlowMatch *match)
684{
685 match->value.tbl_id = ROCKER_OF_DPA_TABLE_ID_MULTICAST_ROUTING;
686 match->value.eth.type = *fc->fields.h_proto;
687 match->value.eth.vlan_id = fc->fields.vlanhdr->h_tci;
688 if (fc->fields.ipv4hdr) {
689 match->value.ipv4.addr.src = fc->fields.ipv4hdr->ip_src;
690 match->value.ipv4.addr.dst = fc->fields.ipv4hdr->ip_dst;
691 }
692 if (fc->fields.ipv6_src_addr) {
693 memcpy(&match->value.ipv6.addr.src, fc->fields.ipv6_src_addr,
694 sizeof(match->value.ipv6.addr.src));
695 }
696 if (fc->fields.ipv6_dst_addr) {
697 memcpy(&match->value.ipv6.addr.dst, fc->fields.ipv6_dst_addr,
698 sizeof(match->value.ipv6.addr.dst));
699 }
700 match->value.width = FLOW_KEY_WIDTH(ipv6.addr.dst);
701}
702
703static void of_dpa_multicast_routing_miss(OfDpaFlowContext *fc)
704{
705 of_dpa_flow_ig_tbl(fc, ROCKER_OF_DPA_TABLE_ID_ACL_POLICY);
706}
707
708static void
709of_dpa_multicast_routing_action_write(OfDpaFlowContext *fc,
710 OfDpaFlow *flow)
711{
712 if (flow->action.write.group_id != ROCKER_GROUP_NONE) {
713 fc->action_set.write.group_id = flow->action.write.group_id;
714 }
715 fc->action_set.write.vlan_id = flow->action.write.vlan_id;
716}
717
718static void of_dpa_acl_build_match(OfDpaFlowContext *fc,
719 OfDpaFlowMatch *match)
720{
721 match->value.tbl_id = ROCKER_OF_DPA_TABLE_ID_ACL_POLICY;
722 match->value.in_pport = fc->in_pport;
723 memcpy(match->value.eth.src.a, fc->fields.ethhdr->h_source,
724 sizeof(match->value.eth.src.a));
725 memcpy(match->value.eth.dst.a, fc->fields.ethhdr->h_dest,
726 sizeof(match->value.eth.dst.a));
727 match->value.eth.type = *fc->fields.h_proto;
728 match->value.eth.vlan_id = fc->fields.vlanhdr->h_tci;
729 match->value.width = FLOW_KEY_WIDTH(eth.type);
730 if (fc->fields.ipv4hdr) {
731 match->value.ip.proto = fc->fields.ipv4hdr->ip_p;
732 match->value.ip.tos = fc->fields.ipv4hdr->ip_tos;
733 match->value.width = FLOW_KEY_WIDTH(ip.tos);
734 } else if (fc->fields.ipv6hdr) {
735 match->value.ip.proto =
736 fc->fields.ipv6hdr->ip6_ctlun.ip6_un1.ip6_un1_nxt;
737 match->value.ip.tos = 0; /* XXX what goes here? */
738 match->value.width = FLOW_KEY_WIDTH(ip.tos);
739 }
740}
741
742static void of_dpa_eg(OfDpaFlowContext *fc);
743static void of_dpa_acl_hit(OfDpaFlowContext *fc,
744 OfDpaFlow *dst_flow)
745{
746 of_dpa_eg(fc);
747}
748
749static void of_dpa_acl_action_write(OfDpaFlowContext *fc,
750 OfDpaFlow *flow)
751{
752 if (flow->action.write.group_id != ROCKER_GROUP_NONE) {
753 fc->action_set.write.group_id = flow->action.write.group_id;
754 }
755}
756
757static void of_dpa_drop(OfDpaFlowContext *fc)
758{
759 /* drop packet */
760}
761
762static OfDpaGroup *of_dpa_group_find(OfDpa *of_dpa,
763 uint32_t group_id)
764{
765 return g_hash_table_lookup(of_dpa->group_tbl, &group_id);
766}
767
768static int of_dpa_group_add(OfDpa *of_dpa, OfDpaGroup *group)
769{
770 g_hash_table_insert(of_dpa->group_tbl, &group->id, group);
771
772 return 0;
773}
774
775#if 0
776static int of_dpa_group_mod(OfDpa *of_dpa, OfDpaGroup *group)
777{
778 OfDpaGroup *old_group = of_dpa_group_find(of_dpa, group->id);
779
780 if (!old_group) {
781 return -ENOENT;
782 }
783
784 /* XXX */
785
786 return 0;
787}
788#endif
789
790static int of_dpa_group_del(OfDpa *of_dpa, OfDpaGroup *group)
791{
792 g_hash_table_remove(of_dpa->group_tbl, &group->id);
793
794 return 0;
795}
796
797#if 0
798static int of_dpa_group_get_stats(OfDpa *of_dpa, uint32_t id)
799{
800 OfDpaGroup *group = of_dpa_group_find(of_dpa, id);
801
802 if (!group) {
803 return -ENOENT;
804 }
805
806 /* XXX get/return stats */
807
808 return 0;
809}
810#endif
811
812static OfDpaGroup *of_dpa_group_alloc(uint32_t id)
813{
814 OfDpaGroup *group = g_malloc0(sizeof(OfDpaGroup));
815
816 if (!group) {
817 return NULL;
818 }
819
820 group->id = id;
821
822 return group;
823}
824
825static void of_dpa_output_l2_interface(OfDpaFlowContext *fc,
826 OfDpaGroup *group)
827{
828 if (group->l2_interface.pop_vlan) {
829 of_dpa_flow_pkt_strip_vlan(fc);
830 }
831
832 /* Note: By default, and as per the OpenFlow 1.3.1
833 * specification, a packet cannot be forwarded back
834 * to the IN_PORT from which it came in. An action
835 * bucket that specifies the particular packet's
836 * egress port is not evaluated.
837 */
838
839 if (group->l2_interface.out_pport == 0) {
840 rx_produce(fc->of_dpa->world, fc->in_pport, fc->iov, fc->iovcnt);
841 } else if (group->l2_interface.out_pport != fc->in_pport) {
842 rocker_port_eg(world_rocker(fc->of_dpa->world),
843 group->l2_interface.out_pport,
844 fc->iov, fc->iovcnt);
845 }
846}
847
848static void of_dpa_output_l2_rewrite(OfDpaFlowContext *fc,
849 OfDpaGroup *group)
850{
851 OfDpaGroup *l2_group =
852 of_dpa_group_find(fc->of_dpa, group->l2_rewrite.group_id);
853
854 if (!l2_group) {
855 return;
856 }
857
858 of_dpa_flow_pkt_hdr_rewrite(fc, group->l2_rewrite.src_mac.a,
859 group->l2_rewrite.dst_mac.a,
860 group->l2_rewrite.vlan_id);
861 of_dpa_output_l2_interface(fc, l2_group);
862}
863
864static void of_dpa_output_l2_flood(OfDpaFlowContext *fc,
865 OfDpaGroup *group)
866{
867 OfDpaGroup *l2_group;
868 int i;
869
870 for (i = 0; i < group->l2_flood.group_count; i++) {
871 of_dpa_flow_pkt_hdr_reset(fc);
872 l2_group = of_dpa_group_find(fc->of_dpa, group->l2_flood.group_ids[i]);
873 if (!l2_group) {
874 continue;
875 }
876 switch (ROCKER_GROUP_TYPE_GET(l2_group->id)) {
877 case ROCKER_OF_DPA_GROUP_TYPE_L2_INTERFACE:
878 of_dpa_output_l2_interface(fc, l2_group);
879 break;
880 case ROCKER_OF_DPA_GROUP_TYPE_L2_REWRITE:
881 of_dpa_output_l2_rewrite(fc, l2_group);
882 break;
883 }
884 }
885}
886
887static void of_dpa_output_l3_unicast(OfDpaFlowContext *fc, OfDpaGroup *group)
888{
889 OfDpaGroup *l2_group =
890 of_dpa_group_find(fc->of_dpa, group->l3_unicast.group_id);
891
892 if (!l2_group) {
893 return;
894 }
895
896 of_dpa_flow_pkt_hdr_rewrite(fc, group->l3_unicast.src_mac.a,
897 group->l3_unicast.dst_mac.a,
898 group->l3_unicast.vlan_id);
899 /* XXX need ttl_check */
900 of_dpa_output_l2_interface(fc, l2_group);
901}
902
903static void of_dpa_eg(OfDpaFlowContext *fc)
904{
905 OfDpaFlowAction *set = &fc->action_set;
906 OfDpaGroup *group;
907 uint32_t group_id;
908
909 /* send a copy of pkt to CPU (controller)? */
910
911 if (set->apply.copy_to_cpu) {
912 group_id = ROCKER_GROUP_L2_INTERFACE(set->apply.vlan_id, 0);
913 group = of_dpa_group_find(fc->of_dpa, group_id);
914 if (group) {
915 of_dpa_output_l2_interface(fc, group);
916 of_dpa_flow_pkt_hdr_reset(fc);
917 }
918 }
919
920 /* process group write actions */
921
922 if (!set->write.group_id) {
923 return;
924 }
925
926 group = of_dpa_group_find(fc->of_dpa, set->write.group_id);
927 if (!group) {
928 return;
929 }
930
931 switch (ROCKER_GROUP_TYPE_GET(group->id)) {
932 case ROCKER_OF_DPA_GROUP_TYPE_L2_INTERFACE:
933 of_dpa_output_l2_interface(fc, group);
934 break;
935 case ROCKER_OF_DPA_GROUP_TYPE_L2_REWRITE:
936 of_dpa_output_l2_rewrite(fc, group);
937 break;
938 case ROCKER_OF_DPA_GROUP_TYPE_L2_FLOOD:
939 case ROCKER_OF_DPA_GROUP_TYPE_L2_MCAST:
940 of_dpa_output_l2_flood(fc, group);
941 break;
942 case ROCKER_OF_DPA_GROUP_TYPE_L3_UCAST:
943 of_dpa_output_l3_unicast(fc, group);
944 break;
945 }
946}
947
948typedef struct of_dpa_flow_tbl_ops {
949 void (*build_match)(OfDpaFlowContext *fc, OfDpaFlowMatch *match);
950 void (*hit)(OfDpaFlowContext *fc, OfDpaFlow *flow);
951 void (*miss)(OfDpaFlowContext *fc);
952 void (*hit_no_goto)(OfDpaFlowContext *fc);
953 void (*action_apply)(OfDpaFlowContext *fc, OfDpaFlow *flow);
954 void (*action_write)(OfDpaFlowContext *fc, OfDpaFlow *flow);
955} OfDpaFlowTblOps;
956
957static OfDpaFlowTblOps of_dpa_tbl_ops[] = {
958 [ROCKER_OF_DPA_TABLE_ID_INGRESS_PORT] = {
959 .build_match = of_dpa_ig_port_build_match,
960 .miss = of_dpa_ig_port_miss,
961 .hit_no_goto = of_dpa_drop,
962 },
963 [ROCKER_OF_DPA_TABLE_ID_VLAN] = {
964 .build_match = of_dpa_vlan_build_match,
965 .hit_no_goto = of_dpa_drop,
966 .action_apply = of_dpa_vlan_insert,
967 },
968 [ROCKER_OF_DPA_TABLE_ID_TERMINATION_MAC] = {
969 .build_match = of_dpa_term_mac_build_match,
970 .miss = of_dpa_term_mac_miss,
971 .hit_no_goto = of_dpa_drop,
972 .action_apply = of_dpa_apply_actions,
973 },
974 [ROCKER_OF_DPA_TABLE_ID_BRIDGING] = {
975 .build_match = of_dpa_bridging_build_match,
976 .hit = of_dpa_bridging_learn,
977 .miss = of_dpa_bridging_miss,
978 .hit_no_goto = of_dpa_drop,
979 .action_apply = of_dpa_apply_actions,
980 .action_write = of_dpa_bridging_action_write,
981 },
982 [ROCKER_OF_DPA_TABLE_ID_UNICAST_ROUTING] = {
983 .build_match = of_dpa_unicast_routing_build_match,
984 .miss = of_dpa_unicast_routing_miss,
985 .hit_no_goto = of_dpa_drop,
986 .action_write = of_dpa_unicast_routing_action_write,
987 },
988 [ROCKER_OF_DPA_TABLE_ID_MULTICAST_ROUTING] = {
989 .build_match = of_dpa_multicast_routing_build_match,
990 .miss = of_dpa_multicast_routing_miss,
991 .hit_no_goto = of_dpa_drop,
992 .action_write = of_dpa_multicast_routing_action_write,
993 },
994 [ROCKER_OF_DPA_TABLE_ID_ACL_POLICY] = {
995 .build_match = of_dpa_acl_build_match,
996 .hit = of_dpa_acl_hit,
997 .miss = of_dpa_eg,
998 .action_apply = of_dpa_apply_actions,
999 .action_write = of_dpa_acl_action_write,
1000 },
1001};
1002
1003static void of_dpa_flow_ig_tbl(OfDpaFlowContext *fc, uint32_t tbl_id)
1004{
1005 OfDpaFlowTblOps *ops = &of_dpa_tbl_ops[tbl_id];
1006 OfDpaFlowMatch match = { { 0, }, };
1007 OfDpaFlow *flow;
1008
1009 if (ops->build_match) {
1010 ops->build_match(fc, &match);
1011 } else {
1012 return;
1013 }
1014
1015 flow = of_dpa_flow_match(fc->of_dpa, &match);
1016 if (!flow) {
1017 if (ops->miss) {
1018 ops->miss(fc);
1019 }
1020 return;
1021 }
1022
1023 flow->stats.hits++;
1024
1025 if (ops->action_apply) {
1026 ops->action_apply(fc, flow);
1027 }
1028
1029 if (ops->action_write) {
1030 ops->action_write(fc, flow);
1031 }
1032
1033 if (ops->hit) {
1034 ops->hit(fc, flow);
1035 }
1036
1037 if (flow->action.goto_tbl) {
1038 of_dpa_flow_ig_tbl(fc, flow->action.goto_tbl);
1039 } else if (ops->hit_no_goto) {
1040 ops->hit_no_goto(fc);
1041 }
1042
1043 /* drop packet */
1044}
1045
1046static ssize_t of_dpa_ig(World *world, uint32_t pport,
1047 const struct iovec *iov, int iovcnt)
1048{
1049 struct iovec iov_copy[iovcnt + 2];
1050 OfDpaFlowContext fc = {
1051 .of_dpa = world_private(world),
1052 .in_pport = pport,
1053 .iov = iov_copy,
1054 .iovcnt = iovcnt + 2,
1055 };
1056
1057 of_dpa_flow_pkt_parse(&fc, iov, iovcnt);
1058 of_dpa_flow_ig_tbl(&fc, ROCKER_OF_DPA_TABLE_ID_INGRESS_PORT);
1059
1060 return iov_size(iov, iovcnt);
1061}
1062
1063#define ROCKER_TUNNEL_LPORT 0x00010000
1064
1065static int of_dpa_cmd_add_ig_port(OfDpaFlow *flow, RockerTlv **flow_tlvs)
1066{
1067 OfDpaFlowKey *key = &flow->key;
1068 OfDpaFlowKey *mask = &flow->mask;
1069 OfDpaFlowAction *action = &flow->action;
1070 bool overlay_tunnel;
1071
1072 if (!flow_tlvs[ROCKER_TLV_OF_DPA_IN_PPORT] ||
1073 !flow_tlvs[ROCKER_TLV_OF_DPA_GOTO_TABLE_ID]) {
1074 return -ROCKER_EINVAL;
1075 }
1076
1077 key->tbl_id = ROCKER_OF_DPA_TABLE_ID_INGRESS_PORT;
1078 key->width = FLOW_KEY_WIDTH(tbl_id);
1079
1080 key->in_pport = rocker_tlv_get_le32(flow_tlvs[ROCKER_TLV_OF_DPA_IN_PPORT]);
1081 if (flow_tlvs[ROCKER_TLV_OF_DPA_IN_PPORT_MASK]) {
1082 mask->in_pport =
1083 rocker_tlv_get_le32(flow_tlvs[ROCKER_TLV_OF_DPA_IN_PPORT_MASK]);
1084 }
1085
1086 overlay_tunnel = !!(key->in_pport & ROCKER_TUNNEL_LPORT);
1087
1088 action->goto_tbl =
1089 rocker_tlv_get_le16(flow_tlvs[ROCKER_TLV_OF_DPA_GOTO_TABLE_ID]);
1090
1091 if (!overlay_tunnel && action->goto_tbl != ROCKER_OF_DPA_TABLE_ID_VLAN) {
1092 return -ROCKER_EINVAL;
1093 }
1094
1095 if (overlay_tunnel && action->goto_tbl != ROCKER_OF_DPA_TABLE_ID_BRIDGING) {
1096 return -ROCKER_EINVAL;
1097 }
1098
1099 return ROCKER_OK;
1100}
1101
1102static int of_dpa_cmd_add_vlan(OfDpaFlow *flow, RockerTlv **flow_tlvs)
1103{
1104 OfDpaFlowKey *key = &flow->key;
1105 OfDpaFlowKey *mask = &flow->mask;
1106 OfDpaFlowAction *action = &flow->action;
1107 uint32_t port;
1108 bool untagged;
1109
1110 if (!flow_tlvs[ROCKER_TLV_OF_DPA_IN_PPORT] ||
1111 !flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID]) {
1112 DPRINTF("Must give in_pport and vlan_id to install VLAN tbl entry\n");
1113 return -ROCKER_EINVAL;
1114 }
1115
1116 key->tbl_id = ROCKER_OF_DPA_TABLE_ID_VLAN;
1117 key->width = FLOW_KEY_WIDTH(eth.vlan_id);
1118
1119 key->in_pport = rocker_tlv_get_le32(flow_tlvs[ROCKER_TLV_OF_DPA_IN_PPORT]);
1120 if (!fp_port_from_pport(key->in_pport, &port)) {
1121 DPRINTF("in_pport (%d) not a front-panel port\n", key->in_pport);
1122 return -ROCKER_EINVAL;
1123 }
1124 mask->in_pport = 0xffffffff;
1125
1126 key->eth.vlan_id = rocker_tlv_get_u16(flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID]);
1127
1128 if (flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID_MASK]) {
1129 mask->eth.vlan_id =
1130 rocker_tlv_get_u16(flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID_MASK]);
1131 }
1132
1133 if (key->eth.vlan_id) {
1134 untagged = false; /* filtering */
1135 } else {
1136 untagged = true;
1137 }
1138
1139 if (flow_tlvs[ROCKER_TLV_OF_DPA_GOTO_TABLE_ID]) {
1140 action->goto_tbl =
1141 rocker_tlv_get_le16(flow_tlvs[ROCKER_TLV_OF_DPA_GOTO_TABLE_ID]);
1142 if (action->goto_tbl != ROCKER_OF_DPA_TABLE_ID_TERMINATION_MAC) {
1143 DPRINTF("Goto tbl (%d) must be TERM_MAC\n", action->goto_tbl);
1144 return -ROCKER_EINVAL;
1145 }
1146 }
1147
1148 if (untagged) {
1149 if (!flow_tlvs[ROCKER_TLV_OF_DPA_NEW_VLAN_ID]) {
1150 DPRINTF("Must specify new vlan_id if untagged\n");
1151 return -ROCKER_EINVAL;
1152 }
1153 action->apply.new_vlan_id =
1154 rocker_tlv_get_u16(flow_tlvs[ROCKER_TLV_OF_DPA_NEW_VLAN_ID]);
1155 if (1 > ntohs(action->apply.new_vlan_id) ||
1156 ntohs(action->apply.new_vlan_id) > 4095) {
1157 DPRINTF("New vlan_id (%d) must be between 1 and 4095\n",
1158 ntohs(action->apply.new_vlan_id));
1159 return -ROCKER_EINVAL;
1160 }
1161 }
1162
1163 return ROCKER_OK;
1164}
1165
1166static int of_dpa_cmd_add_term_mac(OfDpaFlow *flow, RockerTlv **flow_tlvs)
1167{
1168 OfDpaFlowKey *key = &flow->key;
1169 OfDpaFlowKey *mask = &flow->mask;
1170 OfDpaFlowAction *action = &flow->action;
1171 const MACAddr ipv4_mcast = { .a = { 0x01, 0x00, 0x5e, 0x00, 0x00, 0x00 } };
1172 const MACAddr ipv4_mask = { .a = { 0xff, 0xff, 0xff, 0x80, 0x00, 0x00 } };
1173 const MACAddr ipv6_mcast = { .a = { 0x33, 0x33, 0x00, 0x00, 0x00, 0x00 } };
1174 const MACAddr ipv6_mask = { .a = { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } };
1175 uint32_t port;
1176 bool unicast = false;
1177 bool multicast = false;
1178
1179 if (!flow_tlvs[ROCKER_TLV_OF_DPA_IN_PPORT] ||
1180 !flow_tlvs[ROCKER_TLV_OF_DPA_IN_PPORT_MASK] ||
1181 !flow_tlvs[ROCKER_TLV_OF_DPA_ETHERTYPE] ||
1182 !flow_tlvs[ROCKER_TLV_OF_DPA_DST_MAC] ||
1183 !flow_tlvs[ROCKER_TLV_OF_DPA_DST_MAC_MASK] ||
1184 !flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID] ||
1185 !flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID_MASK]) {
1186 return -ROCKER_EINVAL;
1187 }
1188
1189 key->tbl_id = ROCKER_OF_DPA_TABLE_ID_TERMINATION_MAC;
1190 key->width = FLOW_KEY_WIDTH(eth.type);
1191
1192 key->in_pport = rocker_tlv_get_le32(flow_tlvs[ROCKER_TLV_OF_DPA_IN_PPORT]);
1193 if (!fp_port_from_pport(key->in_pport, &port)) {
1194 return -ROCKER_EINVAL;
1195 }
1196 mask->in_pport =
1197 rocker_tlv_get_le32(flow_tlvs[ROCKER_TLV_OF_DPA_IN_PPORT_MASK]);
1198
1199 key->eth.type = rocker_tlv_get_u16(flow_tlvs[ROCKER_TLV_OF_DPA_ETHERTYPE]);
1200 if (key->eth.type != htons(0x0800) && key->eth.type != htons(0x86dd)) {
1201 return -ROCKER_EINVAL;
1202 }
1203 mask->eth.type = htons(0xffff);
1204
1205 memcpy(key->eth.dst.a,
1206 rocker_tlv_data(flow_tlvs[ROCKER_TLV_OF_DPA_DST_MAC]),
1207 sizeof(key->eth.dst.a));
1208 memcpy(mask->eth.dst.a,
1209 rocker_tlv_data(flow_tlvs[ROCKER_TLV_OF_DPA_DST_MAC_MASK]),
1210 sizeof(mask->eth.dst.a));
1211
1212 if ((key->eth.dst.a[0] & 0x01) == 0x00) {
1213 unicast = true;
1214 }
1215
1216 /* only two wildcard rules are acceptable for IPv4 and IPv6 multicast */
1217 if (memcmp(key->eth.dst.a, ipv4_mcast.a, sizeof(key->eth.dst.a)) == 0 &&
1218 memcmp(mask->eth.dst.a, ipv4_mask.a, sizeof(mask->eth.dst.a)) == 0) {
1219 multicast = true;
1220 }
1221 if (memcmp(key->eth.dst.a, ipv6_mcast.a, sizeof(key->eth.dst.a)) == 0 &&
1222 memcmp(mask->eth.dst.a, ipv6_mask.a, sizeof(mask->eth.dst.a)) == 0) {
1223 multicast = true;
1224 }
1225
1226 if (!unicast && !multicast) {
1227 return -ROCKER_EINVAL;
1228 }
1229
1230 key->eth.vlan_id = rocker_tlv_get_u16(flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID]);
1231 mask->eth.vlan_id =
1232 rocker_tlv_get_u16(flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID_MASK]);
1233
1234 if (flow_tlvs[ROCKER_TLV_OF_DPA_GOTO_TABLE_ID]) {
1235 action->goto_tbl =
1236 rocker_tlv_get_le16(flow_tlvs[ROCKER_TLV_OF_DPA_GOTO_TABLE_ID]);
1237
1238 if (action->goto_tbl != ROCKER_OF_DPA_TABLE_ID_UNICAST_ROUTING &&
1239 action->goto_tbl != ROCKER_OF_DPA_TABLE_ID_MULTICAST_ROUTING) {
1240 return -ROCKER_EINVAL;
1241 }
1242
1243 if (unicast &&
1244 action->goto_tbl != ROCKER_OF_DPA_TABLE_ID_UNICAST_ROUTING) {
1245 return -ROCKER_EINVAL;
1246 }
1247
1248 if (multicast &&
1249 action->goto_tbl != ROCKER_OF_DPA_TABLE_ID_MULTICAST_ROUTING) {
1250 return -ROCKER_EINVAL;
1251 }
1252 }
1253
1254 if (flow_tlvs[ROCKER_TLV_OF_DPA_COPY_CPU_ACTION]) {
1255 action->apply.copy_to_cpu =
1256 rocker_tlv_get_u8(flow_tlvs[ROCKER_TLV_OF_DPA_COPY_CPU_ACTION]);
1257 }
1258
1259 return ROCKER_OK;
1260}
1261
1262static int of_dpa_cmd_add_bridging(OfDpaFlow *flow, RockerTlv **flow_tlvs)
1263{
1264 OfDpaFlowKey *key = &flow->key;
1265 OfDpaFlowKey *mask = &flow->mask;
1266 OfDpaFlowAction *action = &flow->action;
1267 bool unicast = false;
1268 bool dst_mac = false;
1269 bool dst_mac_mask = false;
1270 enum {
1271 BRIDGING_MODE_UNKNOWN,
1272 BRIDGING_MODE_VLAN_UCAST,
1273 BRIDGING_MODE_VLAN_MCAST,
1274 BRIDGING_MODE_VLAN_DFLT,
1275 BRIDGING_MODE_TUNNEL_UCAST,
1276 BRIDGING_MODE_TUNNEL_MCAST,
1277 BRIDGING_MODE_TUNNEL_DFLT,
1278 } mode = BRIDGING_MODE_UNKNOWN;
1279
1280 key->tbl_id = ROCKER_OF_DPA_TABLE_ID_BRIDGING;
1281
1282 if (flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID]) {
1283 key->eth.vlan_id =
1284 rocker_tlv_get_u16(flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID]);
1285 mask->eth.vlan_id = 0xffff;
1286 key->width = FLOW_KEY_WIDTH(eth.vlan_id);
1287 }
1288
1289 if (flow_tlvs[ROCKER_TLV_OF_DPA_TUNNEL_ID]) {
1290 key->tunnel_id =
1291 rocker_tlv_get_le32(flow_tlvs[ROCKER_TLV_OF_DPA_TUNNEL_ID]);
1292 mask->tunnel_id = 0xffffffff;
1293 key->width = FLOW_KEY_WIDTH(tunnel_id);
1294 }
1295
1296 /* can't do VLAN bridging and tunnel bridging at same time */
1297 if (key->eth.vlan_id && key->tunnel_id) {
1298 DPRINTF("can't do VLAN bridging and tunnel bridging at same time\n");
1299 return -ROCKER_EINVAL;
1300 }
1301
1302 if (flow_tlvs[ROCKER_TLV_OF_DPA_DST_MAC]) {
1303 memcpy(key->eth.dst.a,
1304 rocker_tlv_data(flow_tlvs[ROCKER_TLV_OF_DPA_DST_MAC]),
1305 sizeof(key->eth.dst.a));
1306 key->width = FLOW_KEY_WIDTH(eth.dst);
1307 dst_mac = true;
1308 unicast = (key->eth.dst.a[0] & 0x01) == 0x00;
1309 }
1310
1311 if (flow_tlvs[ROCKER_TLV_OF_DPA_DST_MAC_MASK]) {
1312 memcpy(mask->eth.dst.a,
1313 rocker_tlv_data(flow_tlvs[ROCKER_TLV_OF_DPA_DST_MAC_MASK]),
1314 sizeof(mask->eth.dst.a));
1315 key->width = FLOW_KEY_WIDTH(eth.dst);
1316 dst_mac_mask = true;
1317 } else if (flow_tlvs[ROCKER_TLV_OF_DPA_DST_MAC]) {
1318 memcpy(mask->eth.dst.a, ff_mac.a, sizeof(mask->eth.dst.a));
1319 }
1320
1321 if (key->eth.vlan_id) {
1322 if (dst_mac && !dst_mac_mask) {
1323 mode = unicast ? BRIDGING_MODE_VLAN_UCAST :
1324 BRIDGING_MODE_VLAN_MCAST;
1325 } else if ((dst_mac && dst_mac_mask) || !dst_mac) {
1326 mode = BRIDGING_MODE_VLAN_DFLT;
1327 }
1328 } else if (key->tunnel_id) {
1329 if (dst_mac && !dst_mac_mask) {
1330 mode = unicast ? BRIDGING_MODE_TUNNEL_UCAST :
1331 BRIDGING_MODE_TUNNEL_MCAST;
1332 } else if ((dst_mac && dst_mac_mask) || !dst_mac) {
1333 mode = BRIDGING_MODE_TUNNEL_DFLT;
1334 }
1335 }
1336
1337 if (mode == BRIDGING_MODE_UNKNOWN) {
1338 DPRINTF("Unknown bridging mode\n");
1339 return -ROCKER_EINVAL;
1340 }
1341
1342 if (flow_tlvs[ROCKER_TLV_OF_DPA_GOTO_TABLE_ID]) {
1343 action->goto_tbl =
1344 rocker_tlv_get_le16(flow_tlvs[ROCKER_TLV_OF_DPA_GOTO_TABLE_ID]);
1345 if (action->goto_tbl != ROCKER_OF_DPA_TABLE_ID_ACL_POLICY) {
1346 DPRINTF("Briding goto tbl must be ACL policy\n");
1347 return -ROCKER_EINVAL;
1348 }
1349 }
1350
1351 if (flow_tlvs[ROCKER_TLV_OF_DPA_GROUP_ID]) {
1352 action->write.group_id =
1353 rocker_tlv_get_le32(flow_tlvs[ROCKER_TLV_OF_DPA_GROUP_ID]);
1354 switch (mode) {
1355 case BRIDGING_MODE_VLAN_UCAST:
1356 if (ROCKER_GROUP_TYPE_GET(action->write.group_id) !=
1357 ROCKER_OF_DPA_GROUP_TYPE_L2_INTERFACE) {
1358 DPRINTF("Bridging mode vlan ucast needs L2 "
1359 "interface group (0x%08x)\n",
1360 action->write.group_id);
1361 return -ROCKER_EINVAL;
1362 }
1363 break;
1364 case BRIDGING_MODE_VLAN_MCAST:
1365 if (ROCKER_GROUP_TYPE_GET(action->write.group_id) !=
1366 ROCKER_OF_DPA_GROUP_TYPE_L2_MCAST) {
1367 DPRINTF("Bridging mode vlan mcast needs L2 "
1368 "mcast group (0x%08x)\n",
1369 action->write.group_id);
1370 return -ROCKER_EINVAL;
1371 }
1372 break;
1373 case BRIDGING_MODE_VLAN_DFLT:
1374 if (ROCKER_GROUP_TYPE_GET(action->write.group_id) !=
1375 ROCKER_OF_DPA_GROUP_TYPE_L2_FLOOD) {
1376 DPRINTF("Bridging mode vlan dflt needs L2 "
1377 "flood group (0x%08x)\n",
1378 action->write.group_id);
1379 return -ROCKER_EINVAL;
1380 }
1381 break;
1382 case BRIDGING_MODE_TUNNEL_MCAST:
1383 if (ROCKER_GROUP_TYPE_GET(action->write.group_id) !=
1384 ROCKER_OF_DPA_GROUP_TYPE_L2_OVERLAY) {
1385 DPRINTF("Bridging mode tunnel mcast needs L2 "
1386 "overlay group (0x%08x)\n",
1387 action->write.group_id);
1388 return -ROCKER_EINVAL;
1389 }
1390 break;
1391 case BRIDGING_MODE_TUNNEL_DFLT:
1392 if (ROCKER_GROUP_TYPE_GET(action->write.group_id) !=
1393 ROCKER_OF_DPA_GROUP_TYPE_L2_OVERLAY) {
1394 DPRINTF("Bridging mode tunnel dflt needs L2 "
1395 "overlay group (0x%08x)\n",
1396 action->write.group_id);
1397 return -ROCKER_EINVAL;
1398 }
1399 break;
1400 default:
1401 return -ROCKER_EINVAL;
1402 }
1403 }
1404
1405 if (flow_tlvs[ROCKER_TLV_OF_DPA_TUNNEL_LPORT]) {
1406 action->write.tun_log_lport =
1407 rocker_tlv_get_le32(flow_tlvs[ROCKER_TLV_OF_DPA_TUNNEL_LPORT]);
1408 if (mode != BRIDGING_MODE_TUNNEL_UCAST) {
1409 DPRINTF("Have tunnel logical port but not "
1410 "in bridging tunnel mode\n");
1411 return -ROCKER_EINVAL;
1412 }
1413 }
1414
1415 if (flow_tlvs[ROCKER_TLV_OF_DPA_COPY_CPU_ACTION]) {
1416 action->apply.copy_to_cpu =
1417 rocker_tlv_get_u8(flow_tlvs[ROCKER_TLV_OF_DPA_COPY_CPU_ACTION]);
1418 }
1419
1420 return ROCKER_OK;
1421}
1422
1423static int of_dpa_cmd_add_unicast_routing(OfDpaFlow *flow,
1424 RockerTlv **flow_tlvs)
1425{
1426 OfDpaFlowKey *key = &flow->key;
1427 OfDpaFlowKey *mask = &flow->mask;
1428 OfDpaFlowAction *action = &flow->action;
1429 enum {
1430 UNICAST_ROUTING_MODE_UNKNOWN,
1431 UNICAST_ROUTING_MODE_IPV4,
1432 UNICAST_ROUTING_MODE_IPV6,
1433 } mode = UNICAST_ROUTING_MODE_UNKNOWN;
1434 uint8_t type;
1435
1436 if (!flow_tlvs[ROCKER_TLV_OF_DPA_ETHERTYPE]) {
1437 return -ROCKER_EINVAL;
1438 }
1439
1440 key->tbl_id = ROCKER_OF_DPA_TABLE_ID_UNICAST_ROUTING;
1441 key->width = FLOW_KEY_WIDTH(ipv6.addr.dst);
1442
1443 key->eth.type = rocker_tlv_get_u16(flow_tlvs[ROCKER_TLV_OF_DPA_ETHERTYPE]);
1444 switch (ntohs(key->eth.type)) {
1445 case 0x0800:
1446 mode = UNICAST_ROUTING_MODE_IPV4;
1447 break;
1448 case 0x86dd:
1449 mode = UNICAST_ROUTING_MODE_IPV6;
1450 break;
1451 default:
1452 return -ROCKER_EINVAL;
1453 }
1454 mask->eth.type = htons(0xffff);
1455
1456 switch (mode) {
1457 case UNICAST_ROUTING_MODE_IPV4:
1458 if (!flow_tlvs[ROCKER_TLV_OF_DPA_DST_IP]) {
1459 return -ROCKER_EINVAL;
1460 }
1461 key->ipv4.addr.dst =
1462 rocker_tlv_get_u32(flow_tlvs[ROCKER_TLV_OF_DPA_DST_IP]);
1463 if (ipv4_addr_is_multicast(key->ipv4.addr.dst)) {
1464 return -ROCKER_EINVAL;
1465 }
1466 flow->lpm = of_dpa_mask2prefix(htonl(0xffffffff));
1467 if (flow_tlvs[ROCKER_TLV_OF_DPA_DST_IP_MASK]) {
1468 mask->ipv4.addr.dst =
1469 rocker_tlv_get_u32(flow_tlvs[ROCKER_TLV_OF_DPA_DST_IP_MASK]);
1470 flow->lpm = of_dpa_mask2prefix(mask->ipv4.addr.dst);
1471 }
1472 break;
1473 case UNICAST_ROUTING_MODE_IPV6:
1474 if (!flow_tlvs[ROCKER_TLV_OF_DPA_DST_IPV6]) {
1475 return -ROCKER_EINVAL;
1476 }
1477 memcpy(&key->ipv6.addr.dst,
1478 rocker_tlv_data(flow_tlvs[ROCKER_TLV_OF_DPA_DST_IPV6]),
1479 sizeof(key->ipv6.addr.dst));
1480 if (ipv6_addr_is_multicast(&key->ipv6.addr.dst)) {
1481 return -ROCKER_EINVAL;
1482 }
1483 if (flow_tlvs[ROCKER_TLV_OF_DPA_DST_IPV6_MASK]) {
1484 memcpy(&mask->ipv6.addr.dst,
1485 rocker_tlv_data(flow_tlvs[ROCKER_TLV_OF_DPA_DST_IPV6_MASK]),
1486 sizeof(mask->ipv6.addr.dst));
1487 }
1488 break;
1489 default:
1490 return -ROCKER_EINVAL;
1491 }
1492
1493 if (flow_tlvs[ROCKER_TLV_OF_DPA_GOTO_TABLE_ID]) {
1494 action->goto_tbl =
1495 rocker_tlv_get_le16(flow_tlvs[ROCKER_TLV_OF_DPA_GOTO_TABLE_ID]);
1496 if (action->goto_tbl != ROCKER_OF_DPA_TABLE_ID_ACL_POLICY) {
1497 return -ROCKER_EINVAL;
1498 }
1499 }
1500
1501 if (flow_tlvs[ROCKER_TLV_OF_DPA_GROUP_ID]) {
1502 action->write.group_id =
1503 rocker_tlv_get_le32(flow_tlvs[ROCKER_TLV_OF_DPA_GROUP_ID]);
1504 type = ROCKER_GROUP_TYPE_GET(action->write.group_id);
1505 if (type != ROCKER_OF_DPA_GROUP_TYPE_L2_INTERFACE &&
1506 type != ROCKER_OF_DPA_GROUP_TYPE_L3_UCAST &&
1507 type != ROCKER_OF_DPA_GROUP_TYPE_L3_ECMP) {
1508 return -ROCKER_EINVAL;
1509 }
1510 }
1511
1512 return ROCKER_OK;
1513}
1514
1515static int of_dpa_cmd_add_multicast_routing(OfDpaFlow *flow,
1516 RockerTlv **flow_tlvs)
1517{
1518 OfDpaFlowKey *key = &flow->key;
1519 OfDpaFlowKey *mask = &flow->mask;
1520 OfDpaFlowAction *action = &flow->action;
1521 enum {
1522 MULTICAST_ROUTING_MODE_UNKNOWN,
1523 MULTICAST_ROUTING_MODE_IPV4,
1524 MULTICAST_ROUTING_MODE_IPV6,
1525 } mode = MULTICAST_ROUTING_MODE_UNKNOWN;
1526
1527 if (!flow_tlvs[ROCKER_TLV_OF_DPA_ETHERTYPE] ||
1528 !flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID]) {
1529 return -ROCKER_EINVAL;
1530 }
1531
1532 key->tbl_id = ROCKER_OF_DPA_TABLE_ID_MULTICAST_ROUTING;
1533 key->width = FLOW_KEY_WIDTH(ipv6.addr.dst);
1534
1535 key->eth.type = rocker_tlv_get_u16(flow_tlvs[ROCKER_TLV_OF_DPA_ETHERTYPE]);
1536 switch (ntohs(key->eth.type)) {
1537 case 0x0800:
1538 mode = MULTICAST_ROUTING_MODE_IPV4;
1539 break;
1540 case 0x86dd:
1541 mode = MULTICAST_ROUTING_MODE_IPV6;
1542 break;
1543 default:
1544 return -ROCKER_EINVAL;
1545 }
1546
1547 key->eth.vlan_id = rocker_tlv_get_u16(flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID]);
1548
1549 switch (mode) {
1550 case MULTICAST_ROUTING_MODE_IPV4:
1551
1552 if (flow_tlvs[ROCKER_TLV_OF_DPA_SRC_IP]) {
1553 key->ipv4.addr.src =
1554 rocker_tlv_get_u32(flow_tlvs[ROCKER_TLV_OF_DPA_SRC_IP]);
1555 }
1556
1557 if (flow_tlvs[ROCKER_TLV_OF_DPA_SRC_IP_MASK]) {
1558 mask->ipv4.addr.src =
1559 rocker_tlv_get_u32(flow_tlvs[ROCKER_TLV_OF_DPA_SRC_IP_MASK]);
1560 }
1561
1562 if (!flow_tlvs[ROCKER_TLV_OF_DPA_SRC_IP]) {
1563 if (mask->ipv4.addr.src != 0) {
1564 return -ROCKER_EINVAL;
1565 }
1566 }
1567
1568 if (!flow_tlvs[ROCKER_TLV_OF_DPA_DST_IP]) {
1569 return -ROCKER_EINVAL;
1570 }
1571
1572 key->ipv4.addr.dst =
1573 rocker_tlv_get_u32(flow_tlvs[ROCKER_TLV_OF_DPA_DST_IP]);
1574 if (!ipv4_addr_is_multicast(key->ipv4.addr.dst)) {
1575 return -ROCKER_EINVAL;
1576 }
1577
1578 break;
1579
1580 case MULTICAST_ROUTING_MODE_IPV6:
1581
1582 if (flow_tlvs[ROCKER_TLV_OF_DPA_SRC_IPV6]) {
1583 memcpy(&key->ipv6.addr.src,
1584 rocker_tlv_data(flow_tlvs[ROCKER_TLV_OF_DPA_SRC_IPV6]),
1585 sizeof(key->ipv6.addr.src));
1586 }
1587
1588 if (flow_tlvs[ROCKER_TLV_OF_DPA_SRC_IPV6_MASK]) {
1589 memcpy(&mask->ipv6.addr.src,
1590 rocker_tlv_data(flow_tlvs[ROCKER_TLV_OF_DPA_SRC_IPV6_MASK]),
1591 sizeof(mask->ipv6.addr.src));
1592 }
1593
1594 if (!flow_tlvs[ROCKER_TLV_OF_DPA_SRC_IPV6]) {
1595 if (mask->ipv6.addr.src.addr32[0] != 0 &&
1596 mask->ipv6.addr.src.addr32[1] != 0 &&
1597 mask->ipv6.addr.src.addr32[2] != 0 &&
1598 mask->ipv6.addr.src.addr32[3] != 0) {
1599 return -ROCKER_EINVAL;
1600 }
1601 }
1602
1603 if (!flow_tlvs[ROCKER_TLV_OF_DPA_DST_IPV6]) {
1604 return -ROCKER_EINVAL;
1605 }
1606
1607 memcpy(&key->ipv6.addr.dst,
1608 rocker_tlv_data(flow_tlvs[ROCKER_TLV_OF_DPA_DST_IPV6]),
1609 sizeof(key->ipv6.addr.dst));
1610 if (!ipv6_addr_is_multicast(&key->ipv6.addr.dst)) {
1611 return -ROCKER_EINVAL;
1612 }
1613
1614 break;
1615
1616 default:
1617 return -ROCKER_EINVAL;
1618 }
1619
1620 if (flow_tlvs[ROCKER_TLV_OF_DPA_GOTO_TABLE_ID]) {
1621 action->goto_tbl =
1622 rocker_tlv_get_le16(flow_tlvs[ROCKER_TLV_OF_DPA_GOTO_TABLE_ID]);
1623 if (action->goto_tbl != ROCKER_OF_DPA_TABLE_ID_ACL_POLICY) {
1624 return -ROCKER_EINVAL;
1625 }
1626 }
1627
1628 if (flow_tlvs[ROCKER_TLV_OF_DPA_GROUP_ID]) {
1629 action->write.group_id =
1630 rocker_tlv_get_le32(flow_tlvs[ROCKER_TLV_OF_DPA_GROUP_ID]);
1631 if (ROCKER_GROUP_TYPE_GET(action->write.group_id) !=
1632 ROCKER_OF_DPA_GROUP_TYPE_L3_MCAST) {
1633 return -ROCKER_EINVAL;
1634 }
1635 action->write.vlan_id = key->eth.vlan_id;
1636 }
1637
1638 return ROCKER_OK;
1639}
1640
1641static int of_dpa_cmd_add_acl_ip(OfDpaFlowKey *key, OfDpaFlowKey *mask,
1642 RockerTlv **flow_tlvs)
1643{
1644 key->width = FLOW_KEY_WIDTH(ip.tos);
1645
1646 key->ip.proto = 0;
1647 key->ip.tos = 0;
1648 mask->ip.proto = 0;
1649 mask->ip.tos = 0;
1650
1651 if (flow_tlvs[ROCKER_TLV_OF_DPA_IP_PROTO]) {
1652 key->ip.proto =
1653 rocker_tlv_get_u8(flow_tlvs[ROCKER_TLV_OF_DPA_IP_PROTO]);
1654 }
1655 if (flow_tlvs[ROCKER_TLV_OF_DPA_IP_PROTO_MASK]) {
1656 mask->ip.proto =
1657 rocker_tlv_get_u8(flow_tlvs[ROCKER_TLV_OF_DPA_IP_PROTO_MASK]);
1658 }
1659 if (flow_tlvs[ROCKER_TLV_OF_DPA_IP_DSCP]) {
1660 key->ip.tos =
1661 rocker_tlv_get_u8(flow_tlvs[ROCKER_TLV_OF_DPA_IP_DSCP]);
1662 }
1663 if (flow_tlvs[ROCKER_TLV_OF_DPA_IP_DSCP_MASK]) {
1664 mask->ip.tos =
1665 rocker_tlv_get_u8(flow_tlvs[ROCKER_TLV_OF_DPA_IP_DSCP_MASK]);
1666 }
1667 if (flow_tlvs[ROCKER_TLV_OF_DPA_IP_ECN]) {
1668 key->ip.tos |=
1669 rocker_tlv_get_u8(flow_tlvs[ROCKER_TLV_OF_DPA_IP_ECN]) << 6;
1670 }
1671 if (flow_tlvs[ROCKER_TLV_OF_DPA_IP_ECN_MASK]) {
1672 mask->ip.tos |=
1673 rocker_tlv_get_u8(flow_tlvs[ROCKER_TLV_OF_DPA_IP_ECN_MASK]) << 6;
1674 }
1675
1676 return ROCKER_OK;
1677}
1678
1679static int of_dpa_cmd_add_acl(OfDpaFlow *flow, RockerTlv **flow_tlvs)
1680{
1681 OfDpaFlowKey *key = &flow->key;
1682 OfDpaFlowKey *mask = &flow->mask;
1683 OfDpaFlowAction *action = &flow->action;
1684 enum {
1685 ACL_MODE_UNKNOWN,
1686 ACL_MODE_IPV4_VLAN,
1687 ACL_MODE_IPV6_VLAN,
1688 ACL_MODE_IPV4_TENANT,
1689 ACL_MODE_IPV6_TENANT,
1690 ACL_MODE_NON_IP_VLAN,
1691 ACL_MODE_NON_IP_TENANT,
1692 ACL_MODE_ANY_VLAN,
1693 ACL_MODE_ANY_TENANT,
1694 } mode = ACL_MODE_UNKNOWN;
1695 int err = ROCKER_OK;
1696
1697 if (!flow_tlvs[ROCKER_TLV_OF_DPA_IN_PPORT] ||
1698 !flow_tlvs[ROCKER_TLV_OF_DPA_ETHERTYPE]) {
1699 return -ROCKER_EINVAL;
1700 }
1701
1702 if (flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID] &&
1703 flow_tlvs[ROCKER_TLV_OF_DPA_TUNNEL_ID]) {
1704 return -ROCKER_EINVAL;
1705 }
1706
1707 key->tbl_id = ROCKER_OF_DPA_TABLE_ID_ACL_POLICY;
1708 key->width = FLOW_KEY_WIDTH(eth.type);
1709
1710 key->in_pport = rocker_tlv_get_le32(flow_tlvs[ROCKER_TLV_OF_DPA_IN_PPORT]);
1711 if (flow_tlvs[ROCKER_TLV_OF_DPA_IN_PPORT_MASK]) {
1712 mask->in_pport =
1713 rocker_tlv_get_le32(flow_tlvs[ROCKER_TLV_OF_DPA_IN_PPORT_MASK]);
1714 }
1715
1716 if (flow_tlvs[ROCKER_TLV_OF_DPA_SRC_MAC]) {
1717 memcpy(key->eth.src.a,
1718 rocker_tlv_data(flow_tlvs[ROCKER_TLV_OF_DPA_SRC_MAC]),
1719 sizeof(key->eth.src.a));
1720 }
1721
1722 if (flow_tlvs[ROCKER_TLV_OF_DPA_SRC_MAC_MASK]) {
1723 memcpy(mask->eth.src.a,
1724 rocker_tlv_data(flow_tlvs[ROCKER_TLV_OF_DPA_SRC_MAC_MASK]),
1725 sizeof(mask->eth.src.a));
1726 }
1727
1728 if (flow_tlvs[ROCKER_TLV_OF_DPA_DST_MAC]) {
1729 memcpy(key->eth.dst.a,
1730 rocker_tlv_data(flow_tlvs[ROCKER_TLV_OF_DPA_DST_MAC]),
1731 sizeof(key->eth.dst.a));
1732 }
1733
1734 if (flow_tlvs[ROCKER_TLV_OF_DPA_DST_MAC_MASK]) {
1735 memcpy(mask->eth.dst.a,
1736 rocker_tlv_data(flow_tlvs[ROCKER_TLV_OF_DPA_DST_MAC_MASK]),
1737 sizeof(mask->eth.dst.a));
1738 }
1739
1740 key->eth.type = rocker_tlv_get_u16(flow_tlvs[ROCKER_TLV_OF_DPA_ETHERTYPE]);
1741 if (key->eth.type) {
1742 mask->eth.type = 0xffff;
1743 }
1744
1745 if (flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID]) {
1746 key->eth.vlan_id =
1747 rocker_tlv_get_u16(flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID]);
1748 }
1749
1750 if (flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID_MASK]) {
1751 mask->eth.vlan_id =
1752 rocker_tlv_get_u16(flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID_MASK]);
1753 }
1754
1755 switch (ntohs(key->eth.type)) {
1756 case 0x0000:
1757 mode = (key->eth.vlan_id) ? ACL_MODE_ANY_VLAN : ACL_MODE_ANY_TENANT;
1758 break;
1759 case 0x0800:
1760 mode = (key->eth.vlan_id) ? ACL_MODE_IPV4_VLAN : ACL_MODE_IPV4_TENANT;
1761 break;
1762 case 0x86dd:
1763 mode = (key->eth.vlan_id) ? ACL_MODE_IPV6_VLAN : ACL_MODE_IPV6_TENANT;
1764 break;
1765 default:
1766 mode = (key->eth.vlan_id) ? ACL_MODE_NON_IP_VLAN :
1767 ACL_MODE_NON_IP_TENANT;
1768 break;
1769 }
1770
1771 /* XXX only supporting VLAN modes for now */
1772 if (mode != ACL_MODE_IPV4_VLAN &&
1773 mode != ACL_MODE_IPV6_VLAN &&
1774 mode != ACL_MODE_NON_IP_VLAN &&
1775 mode != ACL_MODE_ANY_VLAN) {
1776 return -ROCKER_EINVAL;
1777 }
1778
1779 switch (ntohs(key->eth.type)) {
1780 case 0x0800:
1781 case 0x86dd:
1782 err = of_dpa_cmd_add_acl_ip(key, mask, flow_tlvs);
1783 break;
1784 }
1785
1786 if (err) {
1787 return err;
1788 }
1789
1790 if (flow_tlvs[ROCKER_TLV_OF_DPA_GROUP_ID]) {
1791 action->write.group_id =
1792 rocker_tlv_get_le32(flow_tlvs[ROCKER_TLV_OF_DPA_GROUP_ID]);
1793 }
1794
1795 if (flow_tlvs[ROCKER_TLV_OF_DPA_COPY_CPU_ACTION]) {
1796 action->apply.copy_to_cpu =
1797 rocker_tlv_get_u8(flow_tlvs[ROCKER_TLV_OF_DPA_COPY_CPU_ACTION]);
1798 }
1799
1800 return ROCKER_OK;
1801}
1802
1803static int of_dpa_cmd_flow_add_mod(OfDpa *of_dpa, OfDpaFlow *flow,
1804 RockerTlv **flow_tlvs)
1805{
1806 enum rocker_of_dpa_table_id tbl;
1807 int err = ROCKER_OK;
1808
1809 if (!flow_tlvs[ROCKER_TLV_OF_DPA_TABLE_ID] ||
1810 !flow_tlvs[ROCKER_TLV_OF_DPA_PRIORITY] ||
1811 !flow_tlvs[ROCKER_TLV_OF_DPA_HARDTIME]) {
1812 return -ROCKER_EINVAL;
1813 }
1814
1815 tbl = rocker_tlv_get_le16(flow_tlvs[ROCKER_TLV_OF_DPA_TABLE_ID]);
1816 flow->priority = rocker_tlv_get_le32(flow_tlvs[ROCKER_TLV_OF_DPA_PRIORITY]);
1817 flow->hardtime = rocker_tlv_get_le32(flow_tlvs[ROCKER_TLV_OF_DPA_HARDTIME]);
1818
1819 if (flow_tlvs[ROCKER_TLV_OF_DPA_IDLETIME]) {
1820 if (tbl == ROCKER_OF_DPA_TABLE_ID_INGRESS_PORT ||
1821 tbl == ROCKER_OF_DPA_TABLE_ID_VLAN ||
1822 tbl == ROCKER_OF_DPA_TABLE_ID_TERMINATION_MAC) {
1823 return -ROCKER_EINVAL;
1824 }
1825 flow->idletime =
1826 rocker_tlv_get_le32(flow_tlvs[ROCKER_TLV_OF_DPA_IDLETIME]);
1827 }
1828
1829 switch (tbl) {
1830 case ROCKER_OF_DPA_TABLE_ID_INGRESS_PORT:
1831 err = of_dpa_cmd_add_ig_port(flow, flow_tlvs);
1832 break;
1833 case ROCKER_OF_DPA_TABLE_ID_VLAN:
1834 err = of_dpa_cmd_add_vlan(flow, flow_tlvs);
1835 break;
1836 case ROCKER_OF_DPA_TABLE_ID_TERMINATION_MAC:
1837 err = of_dpa_cmd_add_term_mac(flow, flow_tlvs);
1838 break;
1839 case ROCKER_OF_DPA_TABLE_ID_BRIDGING:
1840 err = of_dpa_cmd_add_bridging(flow, flow_tlvs);
1841 break;
1842 case ROCKER_OF_DPA_TABLE_ID_UNICAST_ROUTING:
1843 err = of_dpa_cmd_add_unicast_routing(flow, flow_tlvs);
1844 break;
1845 case ROCKER_OF_DPA_TABLE_ID_MULTICAST_ROUTING:
1846 err = of_dpa_cmd_add_multicast_routing(flow, flow_tlvs);
1847 break;
1848 case ROCKER_OF_DPA_TABLE_ID_ACL_POLICY:
1849 err = of_dpa_cmd_add_acl(flow, flow_tlvs);
1850 break;
1851 }
1852
1853 return err;
1854}
1855
1856static int of_dpa_cmd_flow_add(OfDpa *of_dpa, uint64_t cookie,
1857 RockerTlv **flow_tlvs)
1858{
1859 OfDpaFlow *flow = of_dpa_flow_find(of_dpa, cookie);
1860 int err = ROCKER_OK;
1861
1862 if (flow) {
1863 return -ROCKER_EEXIST;
1864 }
1865
1866 flow = of_dpa_flow_alloc(cookie);
1867 if (!flow) {
1868 return -ROCKER_ENOMEM;
1869 }
1870
1871 err = of_dpa_cmd_flow_add_mod(of_dpa, flow, flow_tlvs);
1872 if (err) {
1873 g_free(flow);
1874 return err;
1875 }
1876
1877 return of_dpa_flow_add(of_dpa, flow);
1878}
1879
1880static int of_dpa_cmd_flow_mod(OfDpa *of_dpa, uint64_t cookie,
1881 RockerTlv **flow_tlvs)
1882{
1883 OfDpaFlow *flow = of_dpa_flow_find(of_dpa, cookie);
1884
1885 if (!flow) {
1886 return -ROCKER_ENOENT;
1887 }
1888
1889 return of_dpa_cmd_flow_add_mod(of_dpa, flow, flow_tlvs);
1890}
1891
1892static int of_dpa_cmd_flow_del(OfDpa *of_dpa, uint64_t cookie)
1893{
1894 OfDpaFlow *flow = of_dpa_flow_find(of_dpa, cookie);
1895
1896 if (!flow) {
1897 return -ROCKER_ENOENT;
1898 }
1899
1900 of_dpa_flow_del(of_dpa, flow);
1901
1902 return ROCKER_OK;
1903}
1904
1905static int of_dpa_cmd_flow_get_stats(OfDpa *of_dpa, uint64_t cookie,
1906 struct desc_info *info, char *buf)
1907{
1908 OfDpaFlow *flow = of_dpa_flow_find(of_dpa, cookie);
1909 size_t tlv_size;
1910 int64_t now = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) / 1000;
1911 int pos;
1912
1913 if (!flow) {
1914 return -ROCKER_ENOENT;
1915 }
1916
1917 tlv_size = rocker_tlv_total_size(sizeof(uint32_t)) + /* duration */
1918 rocker_tlv_total_size(sizeof(uint64_t)) + /* rx_pkts */
1919 rocker_tlv_total_size(sizeof(uint64_t)); /* tx_ptks */
1920
1921 if (tlv_size > desc_buf_size(info)) {
1922 return -ROCKER_EMSGSIZE;
1923 }
1924
1925 pos = 0;
1926 rocker_tlv_put_le32(buf, &pos, ROCKER_TLV_OF_DPA_FLOW_STAT_DURATION,
1927 (int32_t)(now - flow->stats.install_time));
1928 rocker_tlv_put_le64(buf, &pos, ROCKER_TLV_OF_DPA_FLOW_STAT_RX_PKTS,
1929 flow->stats.rx_pkts);
1930 rocker_tlv_put_le64(buf, &pos, ROCKER_TLV_OF_DPA_FLOW_STAT_TX_PKTS,
1931 flow->stats.tx_pkts);
1932
1933 return desc_set_buf(info, tlv_size);
1934}
1935
1936static int of_dpa_flow_cmd(OfDpa *of_dpa, struct desc_info *info,
1937 char *buf, uint16_t cmd,
1938 RockerTlv **flow_tlvs)
1939{
1940 uint64_t cookie;
1941
1942 if (!flow_tlvs[ROCKER_TLV_OF_DPA_COOKIE]) {
1943 return -ROCKER_EINVAL;
1944 }
1945
1946 cookie = rocker_tlv_get_le64(flow_tlvs[ROCKER_TLV_OF_DPA_COOKIE]);
1947
1948 switch (cmd) {
1949 case ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_ADD:
1950 return of_dpa_cmd_flow_add(of_dpa, cookie, flow_tlvs);
1951 case ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_MOD:
1952 return of_dpa_cmd_flow_mod(of_dpa, cookie, flow_tlvs);
1953 case ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_DEL:
1954 return of_dpa_cmd_flow_del(of_dpa, cookie);
1955 case ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_GET_STATS:
1956 return of_dpa_cmd_flow_get_stats(of_dpa, cookie, info, buf);
1957 }
1958
1959 return -ROCKER_ENOTSUP;
1960}
1961
1962static int of_dpa_cmd_add_l2_interface(OfDpaGroup *group,
1963 RockerTlv **group_tlvs)
1964{
1965 if (!group_tlvs[ROCKER_TLV_OF_DPA_OUT_PPORT] ||
1966 !group_tlvs[ROCKER_TLV_OF_DPA_POP_VLAN]) {
1967 return -ROCKER_EINVAL;
1968 }
1969
1970 group->l2_interface.out_pport =
1971 rocker_tlv_get_le32(group_tlvs[ROCKER_TLV_OF_DPA_OUT_PPORT]);
1972 group->l2_interface.pop_vlan =
1973 rocker_tlv_get_u8(group_tlvs[ROCKER_TLV_OF_DPA_POP_VLAN]);
1974
1975 return ROCKER_OK;
1976}
1977
1978static int of_dpa_cmd_add_l2_rewrite(OfDpa *of_dpa, OfDpaGroup *group,
1979 RockerTlv **group_tlvs)
1980{
1981 OfDpaGroup *l2_interface_group;
1982
1983 if (!group_tlvs[ROCKER_TLV_OF_DPA_GROUP_ID_LOWER]) {
1984 return -ROCKER_EINVAL;
1985 }
1986
1987 group->l2_rewrite.group_id =
1988 rocker_tlv_get_le32(group_tlvs[ROCKER_TLV_OF_DPA_GROUP_ID_LOWER]);
1989
1990 l2_interface_group = of_dpa_group_find(of_dpa, group->l2_rewrite.group_id);
1991 if (!l2_interface_group ||
1992 ROCKER_GROUP_TYPE_GET(l2_interface_group->id) !=
1993 ROCKER_OF_DPA_GROUP_TYPE_L2_INTERFACE) {
1994 DPRINTF("l2 rewrite group needs a valid l2 interface group\n");
1995 return -ROCKER_EINVAL;
1996 }
1997
1998 if (group_tlvs[ROCKER_TLV_OF_DPA_SRC_MAC]) {
1999 memcpy(group->l2_rewrite.src_mac.a,
2000 rocker_tlv_data(group_tlvs[ROCKER_TLV_OF_DPA_SRC_MAC]),
2001 sizeof(group->l2_rewrite.src_mac.a));
2002 }
2003
2004 if (group_tlvs[ROCKER_TLV_OF_DPA_DST_MAC]) {
2005 memcpy(group->l2_rewrite.dst_mac.a,
2006 rocker_tlv_data(group_tlvs[ROCKER_TLV_OF_DPA_DST_MAC]),
2007 sizeof(group->l2_rewrite.dst_mac.a));
2008 }
2009
2010 if (group_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID]) {
2011 group->l2_rewrite.vlan_id =
2012 rocker_tlv_get_u16(group_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID]);
2013 if (ROCKER_GROUP_VLAN_GET(l2_interface_group->id) !=
2014 (ntohs(group->l2_rewrite.vlan_id) & VLAN_VID_MASK)) {
2015 DPRINTF("Set VLAN ID must be same as L2 interface group\n");
2016 return -ROCKER_EINVAL;
2017 }
2018 }
2019
2020 return ROCKER_OK;
2021}
2022
2023static int of_dpa_cmd_add_l2_flood(OfDpa *of_dpa, OfDpaGroup *group,
2024 RockerTlv **group_tlvs)
2025{
2026 OfDpaGroup *l2_group;
2027 RockerTlv **tlvs;
2028 int err;
2029 int i;
2030
2031 if (!group_tlvs[ROCKER_TLV_OF_DPA_GROUP_COUNT] ||
2032 !group_tlvs[ROCKER_TLV_OF_DPA_GROUP_IDS]) {
2033 return -ROCKER_EINVAL;
2034 }
2035
2036 group->l2_flood.group_count =
2037 rocker_tlv_get_le16(group_tlvs[ROCKER_TLV_OF_DPA_GROUP_COUNT]);
2038
2039 tlvs = g_malloc0((group->l2_flood.group_count + 1) *
2040 sizeof(RockerTlv *));
2041 if (!tlvs) {
2042 return -ROCKER_ENOMEM;
2043 }
2044
2045 g_free(group->l2_flood.group_ids);
2046 group->l2_flood.group_ids =
2047 g_malloc0(group->l2_flood.group_count * sizeof(uint32_t));
2048 if (!group->l2_flood.group_ids) {
2049 err = -ROCKER_ENOMEM;
2050 goto err_out;
2051 }
2052
2053 rocker_tlv_parse_nested(tlvs, group->l2_flood.group_count,
2054 group_tlvs[ROCKER_TLV_OF_DPA_GROUP_IDS]);
2055
2056 for (i = 0; i < group->l2_flood.group_count; i++) {
2057 group->l2_flood.group_ids[i] = rocker_tlv_get_le32(tlvs[i + 1]);
2058 }
2059
2060 /* All of the L2 interface groups referenced by the L2 flood
2061 * must have same VLAN
2062 */
2063
2064 for (i = 0; i < group->l2_flood.group_count; i++) {
2065 l2_group = of_dpa_group_find(of_dpa, group->l2_flood.group_ids[i]);
2066 if (!l2_group) {
2067 continue;
2068 }
2069 if ((ROCKER_GROUP_TYPE_GET(l2_group->id) ==
2070 ROCKER_OF_DPA_GROUP_TYPE_L2_INTERFACE) &&
2071 (ROCKER_GROUP_VLAN_GET(l2_group->id) !=
2072 ROCKER_GROUP_VLAN_GET(group->id))) {
2073 DPRINTF("l2 interface group 0x%08x VLAN doesn't match l2 "
2074 "flood group 0x%08x\n",
2075 group->l2_flood.group_ids[i], group->id);
2076 err = -ROCKER_EINVAL;
2077 goto err_out;
2078 }
2079 }
2080
2081 g_free(tlvs);
2082 return ROCKER_OK;
2083
2084err_out:
2085 group->l2_flood.group_count = 0;
2086 g_free(group->l2_flood.group_ids);
2087 g_free(tlvs);
2088
2089 return err;
2090}
2091
2092static int of_dpa_cmd_add_l3_unicast(OfDpaGroup *group, RockerTlv **group_tlvs)
2093{
2094 if (!group_tlvs[ROCKER_TLV_OF_DPA_GROUP_ID_LOWER]) {
2095 return -ROCKER_EINVAL;
2096 }
2097
2098 group->l3_unicast.group_id =
2099 rocker_tlv_get_le32(group_tlvs[ROCKER_TLV_OF_DPA_GROUP_ID_LOWER]);
2100
2101 if (group_tlvs[ROCKER_TLV_OF_DPA_SRC_MAC]) {
2102 memcpy(group->l3_unicast.src_mac.a,
2103 rocker_tlv_data(group_tlvs[ROCKER_TLV_OF_DPA_SRC_MAC]),
2104 sizeof(group->l3_unicast.src_mac.a));
2105 }
2106
2107 if (group_tlvs[ROCKER_TLV_OF_DPA_DST_MAC]) {
2108 memcpy(group->l3_unicast.dst_mac.a,
2109 rocker_tlv_data(group_tlvs[ROCKER_TLV_OF_DPA_DST_MAC]),
2110 sizeof(group->l3_unicast.dst_mac.a));
2111 }
2112
2113 if (group_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID]) {
2114 group->l3_unicast.vlan_id =
2115 rocker_tlv_get_u16(group_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID]);
2116 }
2117
2118 if (group_tlvs[ROCKER_TLV_OF_DPA_TTL_CHECK]) {
2119 group->l3_unicast.ttl_check =
2120 rocker_tlv_get_u8(group_tlvs[ROCKER_TLV_OF_DPA_TTL_CHECK]);
2121 }
2122
2123 return ROCKER_OK;
2124}
2125
2126static int of_dpa_cmd_group_do(OfDpa *of_dpa, uint32_t group_id,
2127 OfDpaGroup *group, RockerTlv **group_tlvs)
2128{
2129 uint8_t type = ROCKER_GROUP_TYPE_GET(group_id);
2130
2131 switch (type) {
2132 case ROCKER_OF_DPA_GROUP_TYPE_L2_INTERFACE:
2133 return of_dpa_cmd_add_l2_interface(group, group_tlvs);
2134 case ROCKER_OF_DPA_GROUP_TYPE_L2_REWRITE:
2135 return of_dpa_cmd_add_l2_rewrite(of_dpa, group, group_tlvs);
2136 case ROCKER_OF_DPA_GROUP_TYPE_L2_FLOOD:
2137 /* Treat L2 multicast group same as a L2 flood group */
2138 case ROCKER_OF_DPA_GROUP_TYPE_L2_MCAST:
2139 return of_dpa_cmd_add_l2_flood(of_dpa, group, group_tlvs);
2140 case ROCKER_OF_DPA_GROUP_TYPE_L3_UCAST:
2141 return of_dpa_cmd_add_l3_unicast(group, group_tlvs);
2142 }
2143
2144 return -ROCKER_ENOTSUP;
2145}
2146
2147static int of_dpa_cmd_group_add(OfDpa *of_dpa, uint32_t group_id,
2148 RockerTlv **group_tlvs)
2149{
2150 OfDpaGroup *group = of_dpa_group_find(of_dpa, group_id);
2151 int err;
2152
2153 if (group) {
2154 return -ROCKER_EEXIST;
2155 }
2156
2157 group = of_dpa_group_alloc(group_id);
2158 if (!group) {
2159 return -ROCKER_ENOMEM;
2160 }
2161
2162 err = of_dpa_cmd_group_do(of_dpa, group_id, group, group_tlvs);
2163 if (err) {
2164 goto err_cmd_add;
2165 }
2166
2167 err = of_dpa_group_add(of_dpa, group);
2168 if (err) {
2169 goto err_cmd_add;
2170 }
2171
2172 return ROCKER_OK;
2173
2174err_cmd_add:
2175 g_free(group);
2176 return err;
2177}
2178
2179static int of_dpa_cmd_group_mod(OfDpa *of_dpa, uint32_t group_id,
2180 RockerTlv **group_tlvs)
2181{
2182 OfDpaGroup *group = of_dpa_group_find(of_dpa, group_id);
2183
2184 if (!group) {
2185 return -ROCKER_ENOENT;
2186 }
2187
2188 return of_dpa_cmd_group_do(of_dpa, group_id, group, group_tlvs);
2189}
2190
2191static int of_dpa_cmd_group_del(OfDpa *of_dpa, uint32_t group_id)
2192{
2193 OfDpaGroup *group = of_dpa_group_find(of_dpa, group_id);
2194
2195 if (!group) {
2196 return -ROCKER_ENOENT;
2197 }
2198
2199 return of_dpa_group_del(of_dpa, group);
2200}
2201
2202static int of_dpa_cmd_group_get_stats(OfDpa *of_dpa, uint32_t group_id,
2203 struct desc_info *info, char *buf)
2204{
2205 return -ROCKER_ENOTSUP;
2206}
2207
2208static int of_dpa_group_cmd(OfDpa *of_dpa, struct desc_info *info,
2209 char *buf, uint16_t cmd, RockerTlv **group_tlvs)
2210{
2211 uint32_t group_id;
2212
2213 if (!group_tlvs[ROCKER_TLV_OF_DPA_GROUP_ID]) {
2214 return -ROCKER_EINVAL;
2215 }
2216
2217 group_id = rocker_tlv_get_le32(group_tlvs[ROCKER_TLV_OF_DPA_GROUP_ID]);
2218
2219 switch (cmd) {
2220 case ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_ADD:
2221 return of_dpa_cmd_group_add(of_dpa, group_id, group_tlvs);
2222 case ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_MOD:
2223 return of_dpa_cmd_group_mod(of_dpa, group_id, group_tlvs);
2224 case ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_DEL:
2225 return of_dpa_cmd_group_del(of_dpa, group_id);
2226 case ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_GET_STATS:
2227 return of_dpa_cmd_group_get_stats(of_dpa, group_id, info, buf);
2228 }
2229
2230 return -ROCKER_ENOTSUP;
2231}
2232
2233static int of_dpa_cmd(World *world, struct desc_info *info,
2234 char *buf, uint16_t cmd, RockerTlv *cmd_info_tlv)
2235{
2236 OfDpa *of_dpa = world_private(world);
2237 RockerTlv *tlvs[ROCKER_TLV_OF_DPA_MAX + 1];
2238
2239 rocker_tlv_parse_nested(tlvs, ROCKER_TLV_OF_DPA_MAX, cmd_info_tlv);
2240
2241 switch (cmd) {
2242 case ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_ADD:
2243 case ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_MOD:
2244 case ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_DEL:
2245 case ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_GET_STATS:
2246 return of_dpa_flow_cmd(of_dpa, info, buf, cmd, tlvs);
2247 case ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_ADD:
2248 case ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_MOD:
2249 case ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_DEL:
2250 case ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_GET_STATS:
2251 return of_dpa_group_cmd(of_dpa, info, buf, cmd, tlvs);
2252 }
2253
2254 return -ROCKER_ENOTSUP;
2255}
2256
2257static gboolean rocker_int64_equal(gconstpointer v1, gconstpointer v2)
2258{
2259 return *((const uint64_t *)v1) == *((const uint64_t *)v2);
2260}
2261
2262static guint rocker_int64_hash(gconstpointer v)
2263{
2264 return (guint)*(const uint64_t *)v;
2265}
2266
2267static int of_dpa_init(World *world)
2268{
2269 OfDpa *of_dpa = world_private(world);
2270
2271 of_dpa->world = world;
2272
2273 of_dpa->flow_tbl = g_hash_table_new_full(rocker_int64_hash,
2274 rocker_int64_equal,
2275 NULL, g_free);
2276 if (!of_dpa->flow_tbl) {
2277 return -ENOMEM;
2278 }
2279
2280 of_dpa->group_tbl = g_hash_table_new_full(g_int_hash, g_int_equal,
2281 NULL, g_free);
2282 if (!of_dpa->group_tbl) {
2283 goto err_group_tbl;
2284 }
2285
2286 /* XXX hardcode some artificial table max values */
2287 of_dpa->flow_tbl_max_size = 100;
2288 of_dpa->group_tbl_max_size = 100;
2289
2290 return 0;
2291
2292err_group_tbl:
2293 g_hash_table_destroy(of_dpa->flow_tbl);
2294 return -ENOMEM;
2295}
2296
2297static void of_dpa_uninit(World *world)
2298{
2299 OfDpa *of_dpa = world_private(world);
2300
2301 g_hash_table_destroy(of_dpa->group_tbl);
2302 g_hash_table_destroy(of_dpa->flow_tbl);
2303}
2304
fafa4d50
SF
2305struct of_dpa_flow_fill_context {
2306 RockerOfDpaFlowList *list;
2307 uint32_t tbl_id;
2308};
2309
2310static void of_dpa_flow_fill(void *cookie, void *value, void *user_data)
2311{
2312 struct of_dpa_flow *flow = value;
2313 struct of_dpa_flow_key *key = &flow->key;
2314 struct of_dpa_flow_key *mask = &flow->mask;
2315 struct of_dpa_flow_fill_context *flow_context = user_data;
2316 RockerOfDpaFlowList *new;
2317 RockerOfDpaFlow *nflow;
2318 RockerOfDpaFlowKey *nkey;
2319 RockerOfDpaFlowMask *nmask;
2320 RockerOfDpaFlowAction *naction;
2321
2322 if (flow_context->tbl_id != -1 &&
2323 flow_context->tbl_id != key->tbl_id) {
2324 return;
2325 }
2326
2327 new = g_malloc0(sizeof(*new));
2328 nflow = new->value = g_malloc0(sizeof(*nflow));
2329 nkey = nflow->key = g_malloc0(sizeof(*nkey));
2330 nmask = nflow->mask = g_malloc0(sizeof(*nmask));
2331 naction = nflow->action = g_malloc0(sizeof(*naction));
2332
2333 nflow->cookie = flow->cookie;
2334 nflow->hits = flow->stats.hits;
2335 nkey->priority = flow->priority;
2336 nkey->tbl_id = key->tbl_id;
2337
2338 if (key->in_pport || mask->in_pport) {
2339 nkey->has_in_pport = true;
2340 nkey->in_pport = key->in_pport;
2341 }
2342
2343 if (nkey->has_in_pport && mask->in_pport != 0xffffffff) {
2344 nmask->has_in_pport = true;
2345 nmask->in_pport = mask->in_pport;
2346 }
2347
2348 if (key->eth.vlan_id || mask->eth.vlan_id) {
2349 nkey->has_vlan_id = true;
2350 nkey->vlan_id = ntohs(key->eth.vlan_id);
2351 }
2352
2353 if (nkey->has_vlan_id && mask->eth.vlan_id != 0xffff) {
2354 nmask->has_vlan_id = true;
2355 nmask->vlan_id = ntohs(mask->eth.vlan_id);
2356 }
2357
2358 if (key->tunnel_id || mask->tunnel_id) {
2359 nkey->has_tunnel_id = true;
2360 nkey->tunnel_id = key->tunnel_id;
2361 }
2362
2363 if (nkey->has_tunnel_id && mask->tunnel_id != 0xffffffff) {
2364 nmask->has_tunnel_id = true;
2365 nmask->tunnel_id = mask->tunnel_id;
2366 }
2367
2368 if (memcmp(key->eth.src.a, zero_mac.a, ETH_ALEN) ||
2369 memcmp(mask->eth.src.a, zero_mac.a, ETH_ALEN)) {
2370 nkey->has_eth_src = true;
2371 nkey->eth_src = qemu_mac_strdup_printf(key->eth.src.a);
2372 }
2373
2374 if (nkey->has_eth_src && memcmp(mask->eth.src.a, ff_mac.a, ETH_ALEN)) {
2375 nmask->has_eth_src = true;
2376 nmask->eth_src = qemu_mac_strdup_printf(mask->eth.src.a);
2377 }
2378
2379 if (memcmp(key->eth.dst.a, zero_mac.a, ETH_ALEN) ||
2380 memcmp(mask->eth.dst.a, zero_mac.a, ETH_ALEN)) {
2381 nkey->has_eth_dst = true;
2382 nkey->eth_dst = qemu_mac_strdup_printf(key->eth.dst.a);
2383 }
2384
2385 if (nkey->has_eth_dst && memcmp(mask->eth.dst.a, ff_mac.a, ETH_ALEN)) {
2386 nmask->has_eth_dst = true;
2387 nmask->eth_dst = qemu_mac_strdup_printf(mask->eth.dst.a);
2388 }
2389
2390 if (key->eth.type) {
2391
2392 nkey->has_eth_type = true;
2393 nkey->eth_type = ntohs(key->eth.type);
2394
2395 switch (ntohs(key->eth.type)) {
2396 case 0x0800:
2397 case 0x86dd:
2398 if (key->ip.proto || mask->ip.proto) {
2399 nkey->has_ip_proto = true;
2400 nkey->ip_proto = key->ip.proto;
2401 }
2402 if (nkey->has_ip_proto && mask->ip.proto != 0xff) {
2403 nmask->has_ip_proto = true;
2404 nmask->ip_proto = mask->ip.proto;
2405 }
2406 if (key->ip.tos || mask->ip.tos) {
2407 nkey->has_ip_tos = true;
2408 nkey->ip_tos = key->ip.tos;
2409 }
2410 if (nkey->has_ip_tos && mask->ip.tos != 0xff) {
2411 nmask->has_ip_tos = true;
2412 nmask->ip_tos = mask->ip.tos;
2413 }
2414 break;
2415 }
2416
2417 switch (ntohs(key->eth.type)) {
2418 case 0x0800:
2419 if (key->ipv4.addr.dst || mask->ipv4.addr.dst) {
2420 char *dst = inet_ntoa(*(struct in_addr *)&key->ipv4.addr.dst);
2421 int dst_len = of_dpa_mask2prefix(mask->ipv4.addr.dst);
2422 nkey->has_ip_dst = true;
2423 nkey->ip_dst = g_strdup_printf("%s/%d", dst, dst_len);
2424 }
2425 break;
2426 }
2427 }
2428
2429 if (flow->action.goto_tbl) {
2430 naction->has_goto_tbl = true;
2431 naction->goto_tbl = flow->action.goto_tbl;
2432 }
2433
2434 if (flow->action.write.group_id) {
2435 naction->has_group_id = true;
2436 naction->group_id = flow->action.write.group_id;
2437 }
2438
2439 if (flow->action.apply.new_vlan_id) {
2440 naction->has_new_vlan_id = true;
2441 naction->new_vlan_id = flow->action.apply.new_vlan_id;
2442 }
2443
2444 new->next = flow_context->list;
2445 flow_context->list = new;
2446}
2447
2448RockerOfDpaFlowList *qmp_query_rocker_of_dpa_flows(const char *name,
2449 bool has_tbl_id,
2450 uint32_t tbl_id,
2451 Error **errp)
2452{
2453 struct rocker *r;
2454 struct world *w;
2455 struct of_dpa *of_dpa;
2456 struct of_dpa_flow_fill_context fill_context = {
2457 .list = NULL,
2458 .tbl_id = tbl_id,
2459 };
2460
2461 r = rocker_find(name);
2462 if (!r) {
2463 error_set(errp, ERROR_CLASS_GENERIC_ERROR,
2464 "rocker %s not found", name);
2465 return NULL;
2466 }
2467
2468 w = rocker_get_world(r, ROCKER_WORLD_TYPE_OF_DPA);
2469 if (!w) {
2470 error_set(errp, ERROR_CLASS_GENERIC_ERROR,
2471 "rocker %s doesn't have OF-DPA world", name);
2472 return NULL;
2473 }
2474
2475 of_dpa = world_private(w);
2476
2477 g_hash_table_foreach(of_dpa->flow_tbl, of_dpa_flow_fill, &fill_context);
2478
2479 return fill_context.list;
2480}
2481
2482struct of_dpa_group_fill_context {
2483 RockerOfDpaGroupList *list;
2484 uint8_t type;
2485};
2486
2487static void of_dpa_group_fill(void *key, void *value, void *user_data)
2488{
2489 struct of_dpa_group *group = value;
2490 struct of_dpa_group_fill_context *flow_context = user_data;
2491 RockerOfDpaGroupList *new;
2492 RockerOfDpaGroup *ngroup;
2493 struct uint32List *id;
2494 int i;
2495
2496 if (flow_context->type != 9 &&
2497 flow_context->type != ROCKER_GROUP_TYPE_GET(group->id)) {
2498 return;
2499 }
2500
2501 new = g_malloc0(sizeof(*new));
2502 ngroup = new->value = g_malloc0(sizeof(*ngroup));
2503
2504 ngroup->id = group->id;
2505
2506 ngroup->type = ROCKER_GROUP_TYPE_GET(group->id);
2507
2508 switch (ngroup->type) {
2509 case ROCKER_OF_DPA_GROUP_TYPE_L2_INTERFACE:
2510 ngroup->has_vlan_id = true;
2511 ngroup->vlan_id = ROCKER_GROUP_VLAN_GET(group->id);
2512 ngroup->has_pport = true;
2513 ngroup->pport = ROCKER_GROUP_PORT_GET(group->id);
2514 ngroup->has_out_pport = true;
2515 ngroup->out_pport = group->l2_interface.out_pport;
2516 ngroup->has_pop_vlan = true;
2517 ngroup->pop_vlan = group->l2_interface.pop_vlan;
2518 break;
2519 case ROCKER_OF_DPA_GROUP_TYPE_L2_REWRITE:
2520 ngroup->has_index = true;
2521 ngroup->index = ROCKER_GROUP_INDEX_LONG_GET(group->id);
2522 ngroup->has_group_id = true;
2523 ngroup->group_id = group->l2_rewrite.group_id;
2524 if (group->l2_rewrite.vlan_id) {
2525 ngroup->has_set_vlan_id = true;
2526 ngroup->set_vlan_id = ntohs(group->l2_rewrite.vlan_id);
2527 }
2528 break;
2529 if (memcmp(group->l2_rewrite.src_mac.a, zero_mac.a, ETH_ALEN)) {
2530 ngroup->has_set_eth_src = true;
2531 ngroup->set_eth_src =
2532 qemu_mac_strdup_printf(group->l2_rewrite.src_mac.a);
2533 }
2534 if (memcmp(group->l2_rewrite.dst_mac.a, zero_mac.a, ETH_ALEN)) {
2535 ngroup->has_set_eth_dst = true;
2536 ngroup->set_eth_dst =
2537 qemu_mac_strdup_printf(group->l2_rewrite.dst_mac.a);
2538 }
2539 case ROCKER_OF_DPA_GROUP_TYPE_L2_FLOOD:
2540 case ROCKER_OF_DPA_GROUP_TYPE_L2_MCAST:
2541 ngroup->has_vlan_id = true;
2542 ngroup->vlan_id = ROCKER_GROUP_VLAN_GET(group->id);
2543 ngroup->has_index = true;
2544 ngroup->index = ROCKER_GROUP_INDEX_GET(group->id);
2545 for (i = 0; i < group->l2_flood.group_count; i++) {
2546 ngroup->has_group_ids = true;
2547 id = g_malloc0(sizeof(*id));
2548 id->value = group->l2_flood.group_ids[i];
2549 id->next = ngroup->group_ids;
2550 ngroup->group_ids = id;
2551 }
2552 break;
2553 case ROCKER_OF_DPA_GROUP_TYPE_L3_UCAST:
2554 ngroup->has_index = true;
2555 ngroup->index = ROCKER_GROUP_INDEX_LONG_GET(group->id);
2556 ngroup->has_group_id = true;
2557 ngroup->group_id = group->l3_unicast.group_id;
2558 if (group->l3_unicast.vlan_id) {
2559 ngroup->has_set_vlan_id = true;
2560 ngroup->set_vlan_id = ntohs(group->l3_unicast.vlan_id);
2561 }
2562 if (memcmp(group->l3_unicast.src_mac.a, zero_mac.a, ETH_ALEN)) {
2563 ngroup->has_set_eth_src = true;
2564 ngroup->set_eth_src =
2565 qemu_mac_strdup_printf(group->l3_unicast.src_mac.a);
2566 }
2567 if (memcmp(group->l3_unicast.dst_mac.a, zero_mac.a, ETH_ALEN)) {
2568 ngroup->has_set_eth_dst = true;
2569 ngroup->set_eth_dst =
2570 qemu_mac_strdup_printf(group->l3_unicast.dst_mac.a);
2571 }
2572 if (group->l3_unicast.ttl_check) {
2573 ngroup->has_ttl_check = true;
2574 ngroup->ttl_check = group->l3_unicast.ttl_check;
2575 }
2576 break;
2577 }
2578
2579 new->next = flow_context->list;
2580 flow_context->list = new;
2581}
2582
2583RockerOfDpaGroupList *qmp_query_rocker_of_dpa_groups(const char *name,
2584 bool has_type,
2585 uint8_t type,
2586 Error **errp)
2587{
2588 struct rocker *r;
2589 struct world *w;
2590 struct of_dpa *of_dpa;
2591 struct of_dpa_group_fill_context fill_context = {
2592 .list = NULL,
2593 .type = type,
2594 };
2595
2596 r = rocker_find(name);
2597 if (!r) {
2598 error_set(errp, ERROR_CLASS_GENERIC_ERROR,
2599 "rocker %s not found", name);
2600 return NULL;
2601 }
2602
2603 w = rocker_get_world(r, ROCKER_WORLD_TYPE_OF_DPA);
2604 if (!w) {
2605 error_set(errp, ERROR_CLASS_GENERIC_ERROR,
2606 "rocker %s doesn't have OF-DPA world", name);
2607 return NULL;
2608 }
2609
2610 of_dpa = world_private(w);
2611
2612 g_hash_table_foreach(of_dpa->group_tbl, of_dpa_group_fill, &fill_context);
2613
2614 return fill_context.list;
2615}
2616
dc488f88
SF
2617static WorldOps of_dpa_ops = {
2618 .init = of_dpa_init,
2619 .uninit = of_dpa_uninit,
2620 .ig = of_dpa_ig,
2621 .cmd = of_dpa_cmd,
2622};
2623
2624World *of_dpa_world_alloc(Rocker *r)
2625{
2626 return world_alloc(r, sizeof(OfDpa), ROCKER_WORLD_TYPE_OF_DPA, &of_dpa_ops);
2627}