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 }