back to topotato report
topotato coverage report
Current view: top level - zebra - tc_netlink.c (source / functions) Hit Total Coverage
Test: test_mld_basic.py::MLDBasic Lines: 34 381 8.9 %
Date: 2023-02-24 18:38:01 Functions: 3 24 12.5 %

          Line data    Source code
       1             : /*
       2             :  * Zebra Traffic Control (TC) interaction with the kernel using netlink.
       3             :  *
       4             :  * Copyright (C) 2022 Shichu Yang
       5             :  *
       6             :  * This program is free software; you can redistribute it and/or modify it
       7             :  * under the terms of the GNU General Public License as published by the Free
       8             :  * Software Foundation; either version 2 of the License, or (at your option)
       9             :  * any later version.
      10             :  *
      11             :  * This program is distributed in the hope that it will be useful, but WITHOUT
      12             :  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
      13             :  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
      14             :  * more details.
      15             :  *
      16             :  * You should have received a copy of the GNU General Public License along
      17             :  * with this program; see the file COPYING; if not, write to the Free Software
      18             :  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
      19             :  */
      20             : 
      21             : #include <zebra.h>
      22             : 
      23             : #ifdef HAVE_NETLINK
      24             : 
      25             : #include <linux/pkt_cls.h>
      26             : #include <linux/pkt_sched.h>
      27             : #include <netinet/if_ether.h>
      28             : #include <sys/socket.h>
      29             : 
      30             : #include "if.h"
      31             : #include "prefix.h"
      32             : #include "vrf.h"
      33             : 
      34             : #include "zebra/zserv.h"
      35             : #include "zebra/zebra_ns.h"
      36             : #include "zebra/rt.h"
      37             : #include "zebra/interface.h"
      38             : #include "zebra/debug.h"
      39             : #include "zebra/kernel_netlink.h"
      40             : #include "zebra/tc_netlink.h"
      41             : #include "zebra/zebra_errors.h"
      42             : #include "zebra/zebra_dplane.h"
      43             : #include "zebra/zebra_tc.h"
      44             : #include "zebra/zebra_trace.h"
      45             : 
      46             : #define TC_FREQ_DEFAULT (100)
      47             : 
      48             : /* some magic number */
      49             : #define TC_QDISC_MAJOR_ZEBRA (0xbeef0000u)
      50             : #define TC_MINOR_NOCLASS (0xffffu)
      51             : 
      52             : #define TIME_UNITS_PER_SEC (1000000)
      53             : #define xmittime(r, s) (TIME_UNITS_PER_SEC * ((double)(s) / (double)(r)))
      54             : 
      55           0 : static uint32_t tc_get_freq(void)
      56             : {
      57           0 :         int freq = 0;
      58           0 :         FILE *fp = fopen("/proc/net/psched", "r");
      59             : 
      60           0 :         if (fp) {
      61           0 :                 uint32_t nom, denom;
      62             : 
      63           0 :                 if (fscanf(fp, "%*08x%*08x%08x%08x", &nom, &denom) == 2) {
      64           0 :                         if (nom == 1000000)
      65           0 :                                 freq = denom;
      66             :                 }
      67           0 :                 fclose(fp);
      68             :         }
      69             : 
      70           0 :         return freq == 0 ? TC_FREQ_DEFAULT : freq;
      71             : }
      72             : 
      73           0 : static void tc_calc_rate_table(struct tc_ratespec *ratespec, uint32_t *table,
      74             :                                uint32_t mtu)
      75             : {
      76           0 :         if (mtu == 0)
      77           0 :                 mtu = 2047;
      78             : 
      79           0 :         int cell_log = -1;
      80             : 
      81           0 :         if (cell_log < 0) {
      82           0 :                 cell_log = 0;
      83           0 :                 while ((mtu >> cell_log) > 255)
      84           0 :                         cell_log++;
      85             :         }
      86             : 
      87           0 :         for (int i = 0; i < 256; i++)
      88           0 :                 table[i] = xmittime(ratespec->rate, (i + 1) << cell_log);
      89             : 
      90           0 :         ratespec->cell_align = -1;
      91           0 :         ratespec->cell_log = cell_log;
      92           0 :         ratespec->linklayer = TC_LINKLAYER_ETHERNET;
      93           0 : }
      94             : 
      95           0 : static int tc_flower_get_inet_prefix(const struct prefix *prefix,
      96             :                                      struct inet_prefix *addr)
      97             : {
      98           0 :         addr->family = prefix->family;
      99             : 
     100           0 :         if (addr->family == AF_INET) {
     101           0 :                 addr->bytelen = 4;
     102           0 :                 addr->bitlen = prefix->prefixlen;
     103           0 :                 addr->flags = 0;
     104           0 :                 addr->flags |= PREFIXLEN_SPECIFIED;
     105           0 :                 addr->flags |= ADDRTYPE_INET;
     106           0 :                 memcpy(addr->data, prefix->u.val32, sizeof(prefix->u.val32));
     107           0 :         } else if (addr->family == AF_INET6) {
     108           0 :                 addr->bytelen = 16;
     109           0 :                 addr->bitlen = prefix->prefixlen;
     110           0 :                 addr->flags = 0;
     111           0 :                 addr->flags |= PREFIXLEN_SPECIFIED;
     112           0 :                 addr->flags |= ADDRTYPE_INET;
     113           0 :                 memcpy(addr->data, prefix->u.val, sizeof(prefix->u.val));
     114             :         } else {
     115             :                 return -1;
     116             :         }
     117             : 
     118             :         return 0;
     119             : }
     120             : 
     121           0 : static int tc_flower_get_inet_mask(const struct prefix *prefix,
     122             :                                    struct inet_prefix *addr)
     123             : {
     124           0 :         addr->family = prefix->family;
     125             : 
     126           0 :         if (addr->family == AF_INET) {
     127           0 :                 addr->bytelen = 4;
     128           0 :                 addr->bitlen = prefix->prefixlen;
     129           0 :                 addr->flags = 0;
     130           0 :                 addr->flags |= PREFIXLEN_SPECIFIED;
     131           0 :                 addr->flags |= ADDRTYPE_INET;
     132           0 :         } else if (addr->family == AF_INET6) {
     133           0 :                 addr->bytelen = 16;
     134           0 :                 addr->bitlen = prefix->prefixlen;
     135           0 :                 addr->flags = 0;
     136           0 :                 addr->flags |= PREFIXLEN_SPECIFIED;
     137           0 :                 addr->flags |= ADDRTYPE_INET;
     138             :         } else {
     139             :                 return -1;
     140             :         }
     141             : 
     142           0 :         memset(addr->data, 0xff, addr->bytelen);
     143             : 
     144           0 :         int rest = prefix->prefixlen;
     145             : 
     146           0 :         for (int i = 0; i < addr->bytelen / 4; i++) {
     147           0 :                 if (!rest) {
     148           0 :                         addr->data[i] = 0;
     149           0 :                 } else if (rest / 32 >= 1) {
     150           0 :                         rest -= 32;
     151             :                 } else {
     152           0 :                         addr->data[i] <<= 32 - rest;
     153           0 :                         addr->data[i] = htonl(addr->data[i]);
     154           0 :                         rest = 0;
     155             :                 }
     156             :         }
     157             : 
     158             :         return 0;
     159             : }
     160             : 
     161             : /*
     162             :  * Traffic control queue discipline encoding (only "htb" supported)
     163             :  */
     164           0 : static ssize_t netlink_qdisc_msg_encode(int cmd, struct zebra_dplane_ctx *ctx,
     165             :                                         void *data, size_t datalen)
     166             : {
     167           0 :         struct nlsock *nl;
     168           0 :         const char *kind_str = NULL;
     169             : 
     170           0 :         struct rtattr *nest;
     171             : 
     172           0 :         struct {
     173             :                 struct nlmsghdr n;
     174             :                 struct tcmsg t;
     175             :                 char buf[0];
     176           0 :         } *req = (void *)data;
     177             : 
     178           0 :         if (datalen < sizeof(*req))
     179             :                 return 0;
     180             : 
     181           0 :         nl = kernel_netlink_nlsock_lookup(dplane_ctx_get_ns_sock(ctx));
     182             : 
     183           0 :         memset(req, 0, sizeof(*req));
     184             : 
     185           0 :         req->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
     186           0 :         req->n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST;
     187             : 
     188           0 :         req->n.nlmsg_flags |= NLM_F_REPLACE;
     189             : 
     190           0 :         req->n.nlmsg_type = cmd;
     191             : 
     192           0 :         req->n.nlmsg_pid = nl->snl.nl_pid;
     193             : 
     194           0 :         req->t.tcm_family = AF_UNSPEC;
     195           0 :         req->t.tcm_ifindex = dplane_ctx_get_ifindex(ctx);
     196           0 :         req->t.tcm_info = 0;
     197           0 :         req->t.tcm_handle = 0;
     198           0 :         req->t.tcm_parent = TC_H_ROOT;
     199             : 
     200           0 :         if (cmd == RTM_NEWQDISC) {
     201           0 :                 req->t.tcm_handle = TC_H_MAKE(TC_QDISC_MAJOR_ZEBRA, 0);
     202             : 
     203           0 :                 kind_str = dplane_ctx_tc_qdisc_get_kind_str(ctx);
     204             : 
     205           0 :                 nl_attr_put(&req->n, datalen, TCA_KIND, kind_str,
     206           0 :                             strlen(kind_str) + 1);
     207             : 
     208           0 :                 nest = nl_attr_nest(&req->n, datalen, TCA_OPTIONS);
     209             : 
     210           0 :                 switch (dplane_ctx_tc_qdisc_get_kind(ctx)) {
     211           0 :                 case TC_QDISC_HTB: {
     212           0 :                         struct tc_htb_glob htb_glob = {
     213             :                                 .rate2quantum = 10,
     214             :                                 .version = 3,
     215             :                                 .defcls = TC_MINOR_NOCLASS};
     216           0 :                         nl_attr_put(&req->n, datalen, TCA_HTB_INIT, &htb_glob,
     217             :                                     sizeof(htb_glob));
     218           0 :                         break;
     219             :                 }
     220             :                 case TC_QDISC_NOQUEUE:
     221             :                         break;
     222             :                 default:
     223             :                         break;
     224             :                         /* not implemented */
     225             :                 }
     226             : 
     227           0 :                 nl_attr_nest_end(&req->n, nest);
     228             :         } else {
     229             :                 /* ifindex are enough for del/get qdisc */
     230           0 :         }
     231             : 
     232           0 :         return NLMSG_ALIGN(req->n.nlmsg_len);
     233             : }
     234             : 
     235             : /*
     236             :  * Traffic control class encoding
     237             :  */
     238           0 : static ssize_t netlink_tclass_msg_encode(int cmd, struct zebra_dplane_ctx *ctx,
     239             :                                          void *data, size_t datalen)
     240             : {
     241           0 :         enum dplane_op_e op = dplane_ctx_get_op(ctx);
     242             : 
     243           0 :         struct nlsock *nl;
     244           0 :         const char *kind_str = NULL;
     245             : 
     246           0 :         struct rtattr *nest;
     247             : 
     248           0 :         struct {
     249             :                 struct nlmsghdr n;
     250             :                 struct tcmsg t;
     251             :                 char buf[0];
     252           0 :         } *req = (void *)data;
     253             : 
     254           0 :         if (datalen < sizeof(*req))
     255             :                 return 0;
     256             : 
     257           0 :         nl = kernel_netlink_nlsock_lookup(dplane_ctx_get_ns_sock(ctx));
     258             : 
     259           0 :         memset(req, 0, sizeof(*req));
     260             : 
     261           0 :         req->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
     262           0 :         req->n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST;
     263             : 
     264           0 :         if (op == DPLANE_OP_TC_CLASS_UPDATE)
     265           0 :                 req->n.nlmsg_flags |= NLM_F_REPLACE;
     266             : 
     267           0 :         req->n.nlmsg_type = cmd;
     268             : 
     269           0 :         req->n.nlmsg_pid = nl->snl.nl_pid;
     270             : 
     271           0 :         req->t.tcm_family = AF_UNSPEC;
     272           0 :         req->t.tcm_ifindex = dplane_ctx_get_ifindex(ctx);
     273             : 
     274           0 :         req->t.tcm_handle = TC_H_MAKE(TC_QDISC_MAJOR_ZEBRA,
     275             :                                       dplane_ctx_tc_class_get_handle(ctx));
     276           0 :         req->t.tcm_parent = TC_H_MAKE(TC_QDISC_MAJOR_ZEBRA, 0);
     277           0 :         req->t.tcm_info = 0;
     278             : 
     279           0 :         kind_str = dplane_ctx_tc_class_get_kind_str(ctx);
     280             : 
     281           0 :         if (op == DPLANE_OP_TC_CLASS_ADD || op == DPLANE_OP_TC_CLASS_UPDATE) {
     282           0 :                 zlog_debug("netlink tclass encoder: op: %s kind: %s handle: %u",
     283             :                            op == DPLANE_OP_TC_CLASS_UPDATE ? "update" : "add",
     284             :                            kind_str, dplane_ctx_tc_class_get_handle(ctx));
     285             : 
     286           0 :                 nl_attr_put(&req->n, datalen, TCA_KIND, kind_str,
     287           0 :                             strlen(kind_str) + 1);
     288             : 
     289           0 :                 nest = nl_attr_nest(&req->n, datalen, TCA_OPTIONS);
     290             : 
     291           0 :                 switch (dplane_ctx_tc_class_get_kind(ctx)) {
     292           0 :                 case TC_QDISC_HTB: {
     293           0 :                         struct tc_htb_opt htb_opt = {};
     294             : 
     295           0 :                         uint64_t rate = dplane_ctx_tc_class_get_rate(ctx),
     296           0 :                                  ceil = dplane_ctx_tc_class_get_ceil(ctx);
     297             : 
     298           0 :                         uint64_t buffer, cbuffer;
     299             : 
     300             :                         /* TODO: fetch mtu from interface */
     301           0 :                         uint32_t mtu = 1500;
     302             : 
     303           0 :                         uint32_t rtab[256];
     304           0 :                         uint32_t ctab[256];
     305             : 
     306           0 :                         ceil = MAX(rate, ceil);
     307             : 
     308           0 :                         htb_opt.rate.rate = (rate >> 32 != 0) ? ~0U : rate;
     309           0 :                         htb_opt.ceil.rate = (ceil >> 32 != 0) ? ~0U : ceil;
     310             : 
     311           0 :                         buffer = rate / tc_get_freq() + mtu;
     312           0 :                         cbuffer = ceil / tc_get_freq() + mtu;
     313             : 
     314           0 :                         htb_opt.buffer = buffer;
     315           0 :                         htb_opt.cbuffer = cbuffer;
     316             : 
     317           0 :                         tc_calc_rate_table(&htb_opt.rate, rtab, mtu);
     318           0 :                         tc_calc_rate_table(&htb_opt.ceil, ctab, mtu);
     319             : 
     320           0 :                         htb_opt.ceil.mpu = htb_opt.rate.mpu = 0;
     321           0 :                         htb_opt.ceil.overhead = htb_opt.rate.overhead = 0;
     322             : 
     323           0 :                         if (rate >> 32 != 0) {
     324           0 :                                 nl_attr_put(&req->n, datalen, TCA_HTB_RATE64,
     325             :                                             &rate, sizeof(rate));
     326             :                         }
     327             : 
     328           0 :                         if (ceil >> 32 != 0) {
     329           0 :                                 nl_attr_put(&req->n, datalen, TCA_HTB_CEIL64,
     330             :                                             &ceil, sizeof(ceil));
     331             :                         }
     332             : 
     333           0 :                         nl_attr_put(&req->n, datalen, TCA_HTB_PARMS, &htb_opt,
     334             :                                     sizeof(htb_opt));
     335             : 
     336           0 :                         nl_attr_put(&req->n, datalen, TCA_HTB_RTAB, rtab,
     337             :                                     sizeof(rtab));
     338           0 :                         nl_attr_put(&req->n, datalen, TCA_HTB_CTAB, ctab,
     339             :                                     sizeof(ctab));
     340           0 :                         break;
     341             :                 }
     342             :                 default:
     343             :                         break;
     344             :                 }
     345             : 
     346           0 :                 nl_attr_nest_end(&req->n, nest);
     347             :         }
     348             : 
     349           0 :         return NLMSG_ALIGN(req->n.nlmsg_len);
     350             : }
     351             : 
     352           0 : static int netlink_tfilter_flower_port_type(uint8_t ip_proto, bool src)
     353             : {
     354           0 :         if (ip_proto == IPPROTO_TCP)
     355             :                 return src ? TCA_FLOWER_KEY_TCP_SRC : TCA_FLOWER_KEY_TCP_DST;
     356           0 :         else if (ip_proto == IPPROTO_UDP)
     357             :                 return src ? TCA_FLOWER_KEY_UDP_SRC : TCA_FLOWER_KEY_UDP_DST;
     358           0 :         else if (ip_proto == IPPROTO_SCTP)
     359             :                 return src ? TCA_FLOWER_KEY_SCTP_SRC : TCA_FLOWER_KEY_SCTP_DST;
     360             :         else
     361             :                 return -1;
     362             : }
     363             : 
     364           0 : static void netlink_tfilter_flower_put_options(struct nlmsghdr *n,
     365             :                                                size_t datalen,
     366             :                                                struct zebra_dplane_ctx *ctx)
     367             : {
     368           0 :         struct inet_prefix addr;
     369           0 :         uint32_t flags = 0, classid;
     370           0 :         uint8_t protocol = htons(dplane_ctx_tc_filter_get_eth_proto(ctx));
     371           0 :         uint32_t filter_bm = dplane_ctx_tc_filter_get_filter_bm(ctx);
     372             : 
     373           0 :         if (filter_bm & TC_FLOWER_SRC_IP) {
     374           0 :                 const struct prefix *src_p =
     375           0 :                         dplane_ctx_tc_filter_get_src_ip(ctx);
     376             : 
     377           0 :                 if (tc_flower_get_inet_prefix(src_p, &addr) != 0)
     378           0 :                         return;
     379             : 
     380           0 :                 nl_attr_put(n, datalen,
     381           0 :                             (addr.family == AF_INET) ? TCA_FLOWER_KEY_IPV4_SRC
     382             :                                                      : TCA_FLOWER_KEY_IPV6_SRC,
     383           0 :                             addr.data, addr.bytelen);
     384             : 
     385           0 :                 if (tc_flower_get_inet_mask(src_p, &addr) != 0)
     386             :                         return;
     387             : 
     388           0 :                 nl_attr_put(n, datalen,
     389           0 :                             (addr.family == AF_INET)
     390             :                                     ? TCA_FLOWER_KEY_IPV4_SRC_MASK
     391             :                                     : TCA_FLOWER_KEY_IPV6_SRC_MASK,
     392           0 :                             addr.data, addr.bytelen);
     393             :         }
     394             : 
     395           0 :         if (filter_bm & TC_FLOWER_DST_IP) {
     396           0 :                 const struct prefix *dst_p =
     397           0 :                         dplane_ctx_tc_filter_get_dst_ip(ctx);
     398             : 
     399           0 :                 if (tc_flower_get_inet_prefix(dst_p, &addr) != 0)
     400             :                         return;
     401             : 
     402           0 :                 nl_attr_put(n, datalen,
     403           0 :                             (addr.family == AF_INET) ? TCA_FLOWER_KEY_IPV4_DST
     404             :                                                      : TCA_FLOWER_KEY_IPV6_DST,
     405           0 :                             addr.data, addr.bytelen);
     406             : 
     407           0 :                 if (tc_flower_get_inet_mask(dst_p, &addr) != 0)
     408             :                         return;
     409             : 
     410           0 :                 nl_attr_put(n, datalen,
     411           0 :                             (addr.family == AF_INET)
     412             :                                     ? TCA_FLOWER_KEY_IPV4_DST_MASK
     413             :                                     : TCA_FLOWER_KEY_IPV6_DST_MASK,
     414           0 :                             addr.data, addr.bytelen);
     415             :         }
     416             : 
     417           0 :         if (filter_bm & TC_FLOWER_IP_PROTOCOL) {
     418           0 :                 nl_attr_put8(n, datalen, TCA_FLOWER_KEY_IP_PROTO,
     419           0 :                              dplane_ctx_tc_filter_get_ip_proto(ctx));
     420             :         }
     421             : 
     422           0 :         if (filter_bm & TC_FLOWER_SRC_PORT) {
     423           0 :                 uint16_t min, max;
     424             : 
     425           0 :                 min = dplane_ctx_tc_filter_get_src_port_min(ctx);
     426           0 :                 max = dplane_ctx_tc_filter_get_src_port_max(ctx);
     427             : 
     428           0 :                 if (max > min) {
     429           0 :                         nl_attr_put16(n, datalen, TCA_FLOWER_KEY_PORT_SRC_MIN,
     430           0 :                                       htons(min));
     431             : 
     432           0 :                         nl_attr_put16(n, datalen, TCA_FLOWER_KEY_PORT_SRC_MAX,
     433           0 :                                       htons(max));
     434             :                 } else {
     435           0 :                         int type = netlink_tfilter_flower_port_type(
     436           0 :                                 dplane_ctx_tc_filter_get_ip_proto(ctx), true);
     437             : 
     438             :                         if (type < 0)
     439             :                                 return;
     440             : 
     441           0 :                         nl_attr_put16(n, datalen, type, htons(min));
     442             :                 }
     443             :         }
     444             : 
     445           0 :         if (filter_bm & TC_FLOWER_DST_PORT) {
     446           0 :                 uint16_t min = dplane_ctx_tc_filter_get_dst_port_min(ctx),
     447           0 :                          max = dplane_ctx_tc_filter_get_dst_port_max(ctx);
     448             : 
     449           0 :                 if (max > min) {
     450           0 :                         nl_attr_put16(n, datalen, TCA_FLOWER_KEY_PORT_DST_MIN,
     451           0 :                                       htons(min));
     452             : 
     453           0 :                         nl_attr_put16(n, datalen, TCA_FLOWER_KEY_PORT_DST_MAX,
     454           0 :                                       htons(max));
     455             :                 } else {
     456           0 :                         int type = netlink_tfilter_flower_port_type(
     457           0 :                                 dplane_ctx_tc_filter_get_ip_proto(ctx), false);
     458             : 
     459             :                         if (type < 0)
     460             :                                 return;
     461             : 
     462           0 :                         nl_attr_put16(n, datalen, type, htons(min));
     463             :                 }
     464             :         }
     465             : 
     466           0 :         if (filter_bm & TC_FLOWER_DSFIELD) {
     467           0 :                 nl_attr_put8(n, datalen, TCA_FLOWER_KEY_IP_TOS,
     468           0 :                              dplane_ctx_tc_filter_get_dsfield(ctx));
     469           0 :                 nl_attr_put8(n, datalen, TCA_FLOWER_KEY_IP_TOS_MASK,
     470           0 :                              dplane_ctx_tc_filter_get_dsfield_mask(ctx));
     471             :         }
     472             : 
     473           0 :         classid = TC_H_MAKE(TC_QDISC_MAJOR_ZEBRA,
     474             :                             dplane_ctx_tc_filter_get_classid(ctx));
     475           0 :         nl_attr_put32(n, datalen, TCA_FLOWER_CLASSID, classid);
     476             : 
     477           0 :         nl_attr_put32(n, datalen, TCA_FLOWER_FLAGS, flags);
     478             : 
     479           0 :         nl_attr_put16(n, datalen, TCA_FLOWER_KEY_ETH_TYPE, protocol);
     480             : }
     481             : 
     482             : /*
     483             :  * Traffic control filter encoding
     484             :  */
     485           0 : static ssize_t netlink_tfilter_msg_encode(int cmd, struct zebra_dplane_ctx *ctx,
     486             :                                           void *data, size_t datalen)
     487             : {
     488           0 :         enum dplane_op_e op = dplane_ctx_get_op(ctx);
     489             : 
     490           0 :         struct nlsock *nl;
     491           0 :         const char *kind_str = NULL;
     492             : 
     493           0 :         struct rtattr *nest;
     494             : 
     495           0 :         uint16_t priority;
     496           0 :         uint16_t protocol;
     497             : 
     498           0 :         struct {
     499             :                 struct nlmsghdr n;
     500             :                 struct tcmsg t;
     501             :                 char buf[0];
     502           0 :         } *req = (void *)data;
     503             : 
     504           0 :         if (datalen < sizeof(*req))
     505             :                 return 0;
     506             : 
     507           0 :         nl = kernel_netlink_nlsock_lookup(dplane_ctx_get_ns_sock(ctx));
     508             : 
     509           0 :         memset(req, 0, sizeof(*req));
     510             : 
     511           0 :         req->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
     512           0 :         req->n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST;
     513             : 
     514           0 :         if (op == DPLANE_OP_TC_FILTER_UPDATE)
     515           0 :                 req->n.nlmsg_flags |= NLM_F_REPLACE;
     516             : 
     517           0 :         req->n.nlmsg_type = cmd;
     518             : 
     519           0 :         req->n.nlmsg_pid = nl->snl.nl_pid;
     520             : 
     521           0 :         req->t.tcm_family = AF_UNSPEC;
     522           0 :         req->t.tcm_ifindex = dplane_ctx_get_ifindex(ctx);
     523             : 
     524           0 :         priority = dplane_ctx_tc_filter_get_priority(ctx);
     525           0 :         protocol = htons(dplane_ctx_tc_filter_get_eth_proto(ctx));
     526             : 
     527           0 :         req->t.tcm_info = TC_H_MAKE(priority << 16, protocol);
     528           0 :         req->t.tcm_handle = dplane_ctx_tc_filter_get_handle(ctx);
     529           0 :         req->t.tcm_parent = TC_H_MAKE(TC_QDISC_MAJOR_ZEBRA, 0);
     530             : 
     531           0 :         kind_str = dplane_ctx_tc_filter_get_kind_str(ctx);
     532             : 
     533           0 :         if (op == DPLANE_OP_TC_FILTER_ADD || op == DPLANE_OP_TC_FILTER_UPDATE) {
     534           0 :                 nl_attr_put(&req->n, datalen, TCA_KIND, kind_str,
     535           0 :                             strlen(kind_str) + 1);
     536             : 
     537           0 :                 zlog_debug(
     538             :                         "netlink tfilter encoder: op: %s priority: %u protocol: %u kind: %s handle: %u filter_bm: %u ip_proto: %u",
     539             :                         op == DPLANE_OP_TC_FILTER_UPDATE ? "update" : "add",
     540             :                         priority, protocol, kind_str,
     541             :                         dplane_ctx_tc_filter_get_handle(ctx),
     542             :                         dplane_ctx_tc_filter_get_filter_bm(ctx),
     543             :                         dplane_ctx_tc_filter_get_ip_proto(ctx));
     544             : 
     545           0 :                 nest = nl_attr_nest(&req->n, datalen, TCA_OPTIONS);
     546           0 :                 switch (dplane_ctx_tc_filter_get_kind(ctx)) {
     547           0 :                 case TC_FILTER_FLOWER: {
     548           0 :                         netlink_tfilter_flower_put_options(&req->n, datalen,
     549             :                                                            ctx);
     550           0 :                         break;
     551             :                 }
     552             :                 default:
     553             :                         break;
     554             :                 }
     555           0 :                 nl_attr_nest_end(&req->n, nest);
     556             :         }
     557             : 
     558           0 :         return NLMSG_ALIGN(req->n.nlmsg_len);
     559             : }
     560             : 
     561           0 : static ssize_t netlink_newqdisc_msg_encoder(struct zebra_dplane_ctx *ctx,
     562             :                                             void *buf, size_t buflen)
     563             : {
     564           0 :         return netlink_qdisc_msg_encode(RTM_NEWQDISC, ctx, buf, buflen);
     565             : }
     566             : 
     567           0 : static ssize_t netlink_delqdisc_msg_encoder(struct zebra_dplane_ctx *ctx,
     568             :                                             void *buf, size_t buflen)
     569             : {
     570           0 :         return netlink_qdisc_msg_encode(RTM_DELQDISC, ctx, buf, buflen);
     571             : }
     572             : 
     573           0 : static ssize_t netlink_newtclass_msg_encoder(struct zebra_dplane_ctx *ctx,
     574             :                                              void *buf, size_t buflen)
     575             : {
     576           0 :         return netlink_tclass_msg_encode(RTM_NEWTCLASS, ctx, buf, buflen);
     577             : }
     578             : 
     579           0 : static ssize_t netlink_deltclass_msg_encoder(struct zebra_dplane_ctx *ctx,
     580             :                                              void *buf, size_t buflen)
     581             : {
     582           0 :         return netlink_tclass_msg_encode(RTM_DELTCLASS, ctx, buf, buflen);
     583             : }
     584             : 
     585           0 : static ssize_t netlink_newtfilter_msg_encoder(struct zebra_dplane_ctx *ctx,
     586             :                                               void *buf, size_t buflen)
     587             : {
     588           0 :         return netlink_tfilter_msg_encode(RTM_NEWTFILTER, ctx, buf, buflen);
     589             : }
     590             : 
     591           0 : static ssize_t netlink_deltfilter_msg_encoder(struct zebra_dplane_ctx *ctx,
     592             :                                               void *buf, size_t buflen)
     593             : {
     594           0 :         return netlink_tfilter_msg_encode(RTM_DELTFILTER, ctx, buf, buflen);
     595             : }
     596             : 
     597             : enum netlink_msg_status
     598           0 : netlink_put_tc_qdisc_update_msg(struct nl_batch *bth,
     599             :                                 struct zebra_dplane_ctx *ctx)
     600             : {
     601           0 :         enum dplane_op_e op;
     602           0 :         enum netlink_msg_status ret;
     603             : 
     604           0 :         op = dplane_ctx_get_op(ctx);
     605             : 
     606           0 :         if (op == DPLANE_OP_TC_QDISC_INSTALL) {
     607           0 :                 ret = netlink_batch_add_msg(
     608             :                         bth, ctx, netlink_newqdisc_msg_encoder, false);
     609           0 :         } else if (op == DPLANE_OP_TC_QDISC_UNINSTALL) {
     610           0 :                 ret = netlink_batch_add_msg(
     611             :                         bth, ctx, netlink_delqdisc_msg_encoder, false);
     612             :         } else {
     613             :                 return FRR_NETLINK_ERROR;
     614             :         }
     615             : 
     616             :         return ret;
     617             : }
     618             : 
     619             : enum netlink_msg_status
     620           0 : netlink_put_tc_class_update_msg(struct nl_batch *bth,
     621             :                                 struct zebra_dplane_ctx *ctx)
     622             : {
     623           0 :         enum dplane_op_e op;
     624           0 :         enum netlink_msg_status ret;
     625             : 
     626           0 :         op = dplane_ctx_get_op(ctx);
     627             : 
     628           0 :         if (op == DPLANE_OP_TC_CLASS_ADD || op == DPLANE_OP_TC_CLASS_UPDATE) {
     629           0 :                 ret = netlink_batch_add_msg(
     630             :                         bth, ctx, netlink_newtclass_msg_encoder, false);
     631           0 :         } else if (op == DPLANE_OP_TC_CLASS_DELETE) {
     632           0 :                 ret = netlink_batch_add_msg(
     633             :                         bth, ctx, netlink_deltclass_msg_encoder, false);
     634             :         } else {
     635             :                 return FRR_NETLINK_ERROR;
     636             :         }
     637             : 
     638             :         return ret;
     639             : }
     640             : 
     641             : enum netlink_msg_status
     642           0 : netlink_put_tc_filter_update_msg(struct nl_batch *bth,
     643             :                                  struct zebra_dplane_ctx *ctx)
     644             : {
     645           0 :         enum dplane_op_e op;
     646           0 :         enum netlink_msg_status ret;
     647             : 
     648           0 :         op = dplane_ctx_get_op(ctx);
     649             : 
     650           0 :         if (op == DPLANE_OP_TC_FILTER_ADD) {
     651           0 :                 ret = netlink_batch_add_msg(
     652             :                         bth, ctx, netlink_newtfilter_msg_encoder, false);
     653           0 :         } else if (op == DPLANE_OP_TC_FILTER_UPDATE) {
     654             :                 /*
     655             :                  * Replace will fail if either filter type or the number of
     656             :                  * filter options is changed, so DEL then NEW
     657             :                  *
     658             :                  * TFILTER may have refs to TCLASS.
     659             :                  */
     660             : 
     661           0 :                 (void)netlink_batch_add_msg(
     662             :                         bth, ctx, netlink_deltfilter_msg_encoder, false);
     663           0 :                 ret = netlink_batch_add_msg(
     664             :                         bth, ctx, netlink_newtfilter_msg_encoder, false);
     665           0 :         } else if (op == DPLANE_OP_TC_FILTER_DELETE) {
     666           0 :                 ret = netlink_batch_add_msg(
     667             :                         bth, ctx, netlink_deltfilter_msg_encoder, false);
     668             :         } else {
     669             :                 return FRR_NETLINK_ERROR;
     670             :         }
     671             : 
     672             :         return ret;
     673             : }
     674             : 
     675             : /*
     676             :  * Request filters from the kernel
     677             :  */
     678           0 : static int netlink_request_filters(struct zebra_ns *zns, int family, int type,
     679             :                                    ifindex_t ifindex)
     680             : {
     681           0 :         struct {
     682             :                 struct nlmsghdr n;
     683             :                 struct tcmsg tc;
     684             :         } req;
     685             : 
     686           0 :         memset(&req, 0, sizeof(req));
     687           0 :         req.n.nlmsg_type = type;
     688           0 :         req.n.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
     689           0 :         req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
     690           0 :         req.tc.tcm_family = family;
     691           0 :         req.tc.tcm_ifindex = ifindex;
     692             : 
     693           0 :         return netlink_request(&zns->netlink_cmd, &req);
     694             : }
     695             : 
     696             : /*
     697             :  * Request queue discipline from the kernel
     698             :  */
     699           1 : static int netlink_request_qdiscs(struct zebra_ns *zns, int family, int type)
     700             : {
     701           1 :         struct {
     702             :                 struct nlmsghdr n;
     703             :                 struct tcmsg tc;
     704             :         } req;
     705             : 
     706           1 :         memset(&req, 0, sizeof(req));
     707           1 :         req.n.nlmsg_type = type;
     708           1 :         req.n.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
     709           1 :         req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
     710           1 :         req.tc.tcm_family = family;
     711             : 
     712           1 :         return netlink_request(&zns->netlink_cmd, &req);
     713             : }
     714             : 
     715           4 : int netlink_qdisc_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
     716             : {
     717           4 :         struct tcmsg *tcm;
     718           4 :         struct zebra_tc_qdisc qdisc = {};
     719             : 
     720           4 :         int len;
     721           4 :         struct rtattr *tb[TCA_MAX + 1];
     722             : 
     723           4 :         frrtrace(3, frr_zebra, netlink_tc_qdisc_change, h, ns_id, startup);
     724             : 
     725           4 :         len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct tcmsg));
     726             : 
     727           4 :         if (len < 0) {
     728           0 :                 zlog_err(
     729             :                         "%s: Message received from netlink is of a broken size %d %zu",
     730             :                         __func__, h->nlmsg_len,
     731             :                         (size_t)NLMSG_LENGTH(sizeof(struct tcmsg)));
     732           0 :                 return -1;
     733             :         }
     734             : 
     735           4 :         tcm = NLMSG_DATA(h);
     736           4 :         netlink_parse_rtattr(tb, TCA_MAX, TCA_RTA(tcm), len);
     737             : 
     738           4 :         const char *kind_str = (const char *)RTA_DATA(tb[TCA_KIND]);
     739             : 
     740           4 :         enum tc_qdisc_kind kind = tc_qdisc_str2kind(kind_str);
     741             : 
     742           4 :         qdisc.qdisc.ifindex = tcm->tcm_ifindex;
     743             : 
     744           4 :         switch (kind) {
     745             :         case TC_QDISC_NOQUEUE:
     746             :                 /* "noqueue" is the default qdisc */
     747             :                 break;
     748             :         case TC_QDISC_HTB:
     749             :         case TC_QDISC_UNSPEC:
     750             :                 break;
     751             :         }
     752             : 
     753           4 :         if (tb[TCA_OPTIONS] != NULL) {
     754           0 :                 struct rtattr *options[TCA_HTB_MAX + 1];
     755             : 
     756           0 :                 netlink_parse_rtattr_nested(options, TCA_HTB_MAX,
     757             :                                             tb[TCA_OPTIONS]);
     758             : 
     759             :                 /* TODO: more details */
     760             :                 /* struct tc_htb_glob *glob = RTA_DATA(options[TCA_HTB_INIT]);
     761             :                  */
     762             :         }
     763             : 
     764           4 :         if (h->nlmsg_type == RTM_NEWQDISC) {
     765           4 :                 if (startup &&
     766           4 :                     TC_H_MAJ(tcm->tcm_handle) == TC_QDISC_MAJOR_ZEBRA) {
     767           0 :                         enum zebra_dplane_result ret;
     768             : 
     769           0 :                         ret = dplane_tc_qdisc_uninstall(&qdisc);
     770             : 
     771           0 :                         zlog_debug("%s: %s leftover qdisc: ifindex %d kind %s",
     772             :                                    __func__,
     773             :                                    ((ret == ZEBRA_DPLANE_REQUEST_FAILURE)
     774             :                                             ? "Failed to remove"
     775             :                                             : "Removed"),
     776             :                                    qdisc.qdisc.ifindex, kind_str);
     777             :                 }
     778             :         }
     779             : 
     780             :         return 0;
     781             : }
     782             : 
     783           0 : int netlink_tclass_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
     784             : {
     785           0 :         struct tcmsg *tcm;
     786             : 
     787           0 :         int len;
     788           0 :         struct rtattr *tb[TCA_MAX + 1];
     789             : 
     790           0 :         frrtrace(3, frr_zebra, netlink_tc_class_change, h, ns_id, startup);
     791             : 
     792           0 :         len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct tcmsg));
     793             : 
     794           0 :         if (len < 0) {
     795           0 :                 zlog_err(
     796             :                         "%s: Message received from netlink is of a broken size %d %zu",
     797             :                         __func__, h->nlmsg_len,
     798             :                         (size_t)NLMSG_LENGTH(sizeof(struct tcmsg)));
     799           0 :                 return -1;
     800             :         }
     801             : 
     802           0 :         tcm = NLMSG_DATA(h);
     803           0 :         netlink_parse_rtattr(tb, TCA_MAX, TCA_RTA(tcm), len);
     804             : 
     805             : 
     806           0 :         if (tb[TCA_OPTIONS] != NULL) {
     807           0 :                 struct rtattr *options[TCA_HTB_MAX + 1];
     808             : 
     809           0 :                 netlink_parse_rtattr_nested(options, TCA_HTB_MAX,
     810             :                                             tb[TCA_OPTIONS]);
     811             : 
     812             :                 /* TODO: more details */
     813             :                 /* struct tc_htb_opt *opt = RTA_DATA(options[TCA_HTB_PARMS]); */
     814             :         }
     815             : 
     816             :         return 0;
     817             : }
     818             : 
     819           0 : int netlink_tfilter_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
     820             : {
     821           0 :         struct tcmsg *tcm;
     822             : 
     823           0 :         int len;
     824           0 :         struct rtattr *tb[TCA_MAX + 1];
     825             : 
     826           0 :         frrtrace(3, frr_zebra, netlink_tc_filter_change, h, ns_id, startup);
     827             : 
     828           0 :         len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct tcmsg));
     829             : 
     830           0 :         if (len < 0) {
     831           0 :                 zlog_err(
     832             :                         "%s: Message received from netlink is of a broken size %d %zu",
     833             :                         __func__, h->nlmsg_len,
     834             :                         (size_t)NLMSG_LENGTH(sizeof(struct tcmsg)));
     835           0 :                 return -1;
     836             :         }
     837             : 
     838           0 :         tcm = NLMSG_DATA(h);
     839           0 :         netlink_parse_rtattr(tb, TCA_MAX, TCA_RTA(tcm), len);
     840             : 
     841           0 :         return 0;
     842             : }
     843             : 
     844           1 : int netlink_qdisc_read(struct zebra_ns *zns)
     845             : {
     846           1 :         int ret;
     847           1 :         struct zebra_dplane_info dp_info;
     848             : 
     849           1 :         zebra_dplane_info_from_zns(&dp_info, zns, true);
     850             : 
     851           1 :         ret = netlink_request_qdiscs(zns, AF_UNSPEC, RTM_GETQDISC);
     852           1 :         if (ret < 0)
     853             :                 return ret;
     854             : 
     855           1 :         ret = netlink_parse_info(netlink_qdisc_change, &zns->netlink_cmd,
     856             :                                  &dp_info, 0, true);
     857           1 :         if (ret < 0)
     858             :                 return ret;
     859             : 
     860             :         return 0;
     861             : }
     862             : 
     863           0 : int netlink_tfilter_read_for_interface(struct zebra_ns *zns, ifindex_t ifindex)
     864             : {
     865           0 :         int ret;
     866           0 :         struct zebra_dplane_info dp_info;
     867             : 
     868           0 :         zebra_dplane_info_from_zns(&dp_info, zns, true);
     869             : 
     870           0 :         ret = netlink_request_filters(zns, AF_UNSPEC, RTM_GETTFILTER, ifindex);
     871           0 :         if (ret < 0)
     872             :                 return ret;
     873             : 
     874           0 :         ret = netlink_parse_info(netlink_tfilter_change, &zns->netlink_cmd,
     875             :                                  &dp_info, 0, true);
     876           0 :         if (ret < 0)
     877             :                 return ret;
     878             : 
     879             :         return 0;
     880             : }
     881             : 
     882             : #endif /* HAVE_NETLINK */

Generated by: LCOV version v1.16-topotato