linkd

Control plane daemon for unos
git clone git://git.finwo.net/app/linkd
Log | Files | Refs | README

ops.c (5598B)


      1 #define _GNU_SOURCE
      2 #include <arpa/inet.h>
      3 #include <stdio.h>
      4 #include <stdlib.h>
      5 #include <string.h>
      6 #include <sys/socket.h>
      7 
      8 #include "finwo/resp.h"
      9 #include "rxi/log.h"
     10 
     11 #include "dataplane.h"
     12 #include "dataplane/plugin.h"
     13 
     14 #define MAX_ARGV 24
     15 
     16 // Every supporter is called even after one fails, so plugins cannot end up
     17 // disagreeing about what was applied. `optional` failures do not fail the op.
     18 static int broadcast(const char **argv, int argc) {
     19   int failed = 0;
     20   int sent   = 0;
     21 
     22   for (struct dp_plugin *p = dp_plugin_list(); p; p = p->next) {
     23     // Retry a down plugin here: an operation is exactly when we want it back.
     24     if (!p->up) dp_plugin_up(p);
     25     if (!dp_plugin_supports(p, argv[0])) continue;
     26 
     27     sent++;
     28     resp_object *r = dp_plugin_call(p, argv, argc);
     29     if (!r) {
     30       if (!p->optional) failed = 1;
     31       continue;
     32     }
     33     if (r->type == RESPT_ERROR) {
     34       log_error("plugin %s: %s %s: %s", p->target, argv[0],
     35                 argc > 1 ? argv[1] : "", r->u.s ? r->u.s : "error");
     36       if (!p->optional) failed = 1;
     37     }
     38     resp_free(r);
     39   }
     40 
     41   (void)sent;
     42   return failed ? DP_RET_ERROR : DP_RET_OK;
     43 }
     44 
     45 static const char *fec_str(enum dp_fec f) {
     46   switch (f) {
     47     case DP_FEC_OFF:   return "off";
     48     case DP_FEC_AUTO:  return "auto";
     49     case DP_FEC_RS:    return "rs";
     50     case DP_FEC_BASER: return "baser";
     51     default:           return NULL;
     52   }
     53 }
     54 
     55 // dp_addr -> "10.0.0.1/24"
     56 static int addr_str(const struct dp_addr *a, char *out, size_t len) {
     57   char ip[INET6_ADDRSTRLEN];
     58   if (!inet_ntop(a->family, a->addr, ip, sizeof(ip))) return -1;
     59   snprintf(out, len, "%s/%u", ip, a->prefixlen);
     60   return 0;
     61 }
     62 
     63 static int addr_plain(const struct dp_addr *a, char *out, size_t len) {
     64   return inet_ntop(a->family, a->addr, out, (socklen_t)len) ? 0 : -1;
     65 }
     66 
     67 int dp_port_apply(const struct dp_port *port) {
     68   if (!dp_have_plugins()) return DP_RET_OK;
     69 
     70   const char *argv[MAX_ARGV];
     71   char speed[16], autoneg[12];
     72   int  argc = 0;
     73 
     74   argv[argc++] = "PORT";
     75   argv[argc++] = "APPLY";
     76   argv[argc++] = port->name;
     77 
     78   if (port->speed) {
     79     snprintf(speed, sizeof(speed), "%u", port->speed);
     80     argv[argc++] = "speed";
     81     argv[argc++] = speed;
     82   }
     83   const char *f = fec_str(port->fec);
     84   if (f) {
     85     argv[argc++] = "fec";
     86     argv[argc++] = f;
     87   }
     88   if (port->autoneg >= 0) {
     89     snprintf(autoneg, sizeof(autoneg), "%d", port->autoneg);
     90     argv[argc++] = "autoneg";
     91     argv[argc++] = autoneg;
     92   }
     93   return broadcast(argv, argc);
     94 }
     95 
     96 int dp_port_admin(const char *ifname, bool up) {
     97   if (!dp_have_plugins()) return DP_RET_OK;
     98   const char *argv[] = { "PORT", "ADMIN", ifname, up ? "up" : "down" };
     99   return broadcast(argv, 4);
    100 }
    101 
    102 int dp_port_mtu(const char *ifname, uint32_t mtu) {
    103   if (!dp_have_plugins()) return DP_RET_OK;
    104   char m[16];
    105   snprintf(m, sizeof(m), "%u", mtu);
    106   const char *argv[] = { "PORT", "MTU", ifname, m };
    107   return broadcast(argv, 4);
    108 }
    109 
    110 static int rif_op(const char *sub, const struct dp_rif *rif) {
    111   if (!dp_have_plugins()) return DP_RET_OK;
    112   char addr[INET6_ADDRSTRLEN + 8], table[16];
    113   if (addr_str(&rif->addr, addr, sizeof(addr)) != 0) return DP_RET_ERROR;
    114   snprintf(table, sizeof(table), "%u", rif->table);
    115   const char *argv[] = { "RIF", sub, rif->ifname, addr, "table", table };
    116   return broadcast(argv, 6);
    117 }
    118 
    119 int dp_rif_add(const struct dp_rif *rif) { return rif_op("ADD", rif); }
    120 int dp_rif_del(const struct dp_rif *rif) { return rif_op("DEL", rif); }
    121 
    122 static int neigh_op(const char *sub, const struct dp_neigh *n) {
    123   if (!dp_have_plugins()) return DP_RET_OK;
    124   char ip[INET6_ADDRSTRLEN], mac[18], table[16];
    125   if (addr_plain(&n->addr, ip, sizeof(ip)) != 0) return DP_RET_ERROR;
    126   snprintf(mac, sizeof(mac), "%02x:%02x:%02x:%02x:%02x:%02x",
    127            n->mac[0], n->mac[1], n->mac[2], n->mac[3], n->mac[4], n->mac[5]);
    128   snprintf(table, sizeof(table), "%u", n->table);
    129   const char *argv[] = { "NEIGH", sub, n->ifname, ip, mac, "table", table };
    130   return broadcast(argv, 7);
    131 }
    132 
    133 int dp_neigh_add(const struct dp_neigh *n) { return neigh_op("ADD", n); }
    134 int dp_neigh_del(const struct dp_neigh *n) { return neigh_op("DEL", n); }
    135 
    136 // ROUTE ADD <dst> [via <gw> dev <if>]... [metric N] [table N]
    137 // Nexthops are flattened rather than nested so a shell plugin can parse them.
    138 static int route_op(const char *sub, const struct dp_route *r) {
    139   if (!dp_have_plugins()) return DP_RET_OK;
    140 
    141   const char *argv[MAX_ARGV];
    142   char dst[INET6_ADDRSTRLEN + 8];
    143   char gw[4][INET6_ADDRSTRLEN];
    144   char metric[16], table[16];
    145   int  argc = 0;
    146 
    147   if (addr_str(&r->dst, dst, sizeof(dst)) != 0) return DP_RET_ERROR;
    148 
    149   argv[argc++] = "ROUTE";
    150   argv[argc++] = sub;
    151   argv[argc++] = dst;
    152 
    153   size_t nh = r->nh_count;
    154   if (nh > 4) nh = 4; // argv budget; ECMP wider than this is truncated
    155   for (size_t i = 0; i < nh; i++) {
    156     if (argc + 4 >= MAX_ARGV) break;
    157     if (addr_plain(&r->nh[i].gw, gw[i], sizeof(gw[i])) == 0) {
    158       argv[argc++] = "via";
    159       argv[argc++] = gw[i];
    160     }
    161     if (r->nh[i].ifname) {
    162       argv[argc++] = "dev";
    163       argv[argc++] = r->nh[i].ifname;
    164     }
    165   }
    166   if (r->metric) {
    167     snprintf(metric, sizeof(metric), "%u", r->metric);
    168     argv[argc++] = "metric";
    169     argv[argc++] = metric;
    170   }
    171   snprintf(table, sizeof(table), "%u", r->table);
    172   argv[argc++] = "table";
    173   argv[argc++] = table;
    174 
    175   return broadcast(argv, argc);
    176 }
    177 
    178 int dp_route_add(const struct dp_route *r) { return route_op("ADD", r); }
    179 int dp_route_del(const struct dp_route *r) { return route_op("DEL", r); }
    180 
    181 int dp_resync(void) {
    182   if (!dp_have_plugins()) return DP_RET_OK;
    183   const char *argv[] = { "RESYNC" };
    184   return broadcast(argv, 1);
    185 }