X-Git-Url: http://git.cascardo.eti.br/?a=blobdiff_plain;f=lib%2Fovs-router.c;h=d416bdbd2aa1d2afa9f1b906540a18e562ef18f2;hb=d0a46cb4608e632f5028034762f0adde2ce947a0;hp=532487e8fc02ec8dbb878e710208c7b2de10de95;hpb=03ce866e44835e43f04747cfc65b2c592d780165;p=cascardo%2Fovs.git diff --git a/lib/ovs-router.c b/lib/ovs-router.c index 532487e8f..d416bdbd2 100644 --- a/lib/ovs-router.c +++ b/lib/ovs-router.c @@ -15,6 +15,9 @@ */ #include + +#include "ovs-router.h" + #include #include #include @@ -34,9 +37,9 @@ #include "netdev.h" #include "packets.h" #include "seq.h" -#include "ovs-router.h" #include "ovs-thread.h" #include "route-table.h" +#include "tnl-ports.h" #include "unixctl.h" #include "util.h" @@ -46,8 +49,8 @@ static struct classifier cls; struct ovs_router_entry { struct cls_rule cr; char output_bridge[IFNAMSIZ]; - ovs_be32 gw; - ovs_be32 nw_addr; + struct in6_addr gw; + struct in6_addr nw_addr; uint8_t plen; uint8_t priority; }; @@ -63,10 +66,11 @@ ovs_router_entry_cast(const struct cls_rule *cr) } bool -ovs_router_lookup(ovs_be32 ip_dst, char output_bridge[], ovs_be32 *gw) +ovs_router_lookup(const struct in6_addr *ip6_dst, char output_bridge[], + struct in6_addr *gw) { const struct cls_rule *cr; - struct flow flow = {.nw_dst = ip_dst}; + struct flow flow = {.ipv6_dst = *ip6_dst}; cr = classifier_lookup(&cls, CLS_MAX_VERSION, &flow, NULL); if (cr) { @@ -76,6 +80,19 @@ ovs_router_lookup(ovs_be32 ip_dst, char output_bridge[], ovs_be32 *gw) *gw = p->gw; return true; } + return false; +} + +bool +ovs_router_lookup4(ovs_be32 ip_dst, char output_bridge[], ovs_be32 *gw) +{ + struct in6_addr ip6_dst = in6_addr_mapped_ipv4(ip_dst); + struct in6_addr gw6; + + if (ovs_router_lookup(&ip6_dst, output_bridge, &gw6)) { + *gw = in6_addr_get_mapped_ipv4(&gw6); + return true; + } return route_table_fallback_lookup(ip_dst, output_bridge, gw); } @@ -86,133 +103,151 @@ rt_entry_free(struct ovs_router_entry *p) free(p); } -static void rt_init_match(struct match *match, ovs_be32 ip_dst, uint8_t plen) +static void rt_init_match(struct match *match, const struct in6_addr *ip6_dst, + uint8_t plen) { - ovs_be32 mask; + struct in6_addr dst; + struct in6_addr mask; - mask = be32_prefix_mask(plen); + mask = ipv6_create_mask(plen); - ip_dst &= mask; /* Clear out insignificant bits. */ + dst = ipv6_addr_bitand(ip6_dst, &mask); memset(match, 0, sizeof *match); - match->flow.nw_dst = ip_dst; - match->wc.masks.nw_dst = mask; + match->flow.ipv6_dst = dst; + match->wc.masks.ipv6_dst = mask; } static void -ovs_router_insert__(uint8_t priority, ovs_be32 ip_dst, uint8_t plen, - const char output_bridge[], - ovs_be32 gw) +ovs_router_insert__(uint8_t priority, const struct in6_addr *ip6_dst, + uint8_t plen, const char output_bridge[], + const struct in6_addr *gw) { const struct cls_rule *cr; struct ovs_router_entry *p; struct match match; - rt_init_match(&match, ip_dst, plen); + rt_init_match(&match, ip6_dst, plen); p = xzalloc(sizeof *p); ovs_strlcpy(p->output_bridge, output_bridge, sizeof p->output_bridge); - p->gw = gw; - p->nw_addr = match.flow.nw_dst; + if (ipv6_addr_is_set(gw)) { + p->gw = *gw; + } + p->nw_addr = match.flow.ipv6_dst; p->plen = plen; p->priority = priority; /* Longest prefix matches first. */ - cls_rule_init(&p->cr, &match, priority, CLS_MIN_VERSION); + cls_rule_init(&p->cr, &match, priority); ovs_mutex_lock(&mutex); - cr = classifier_replace(&cls, &p->cr, NULL, 0); + cr = classifier_replace(&cls, &p->cr, CLS_MIN_VERSION, NULL, 0); ovs_mutex_unlock(&mutex); if (cr) { /* An old rule with the same match was displaced. */ ovsrcu_postpone(rt_entry_free, ovs_router_entry_cast(cr)); } + tnl_port_map_insert_ipdev(output_bridge); seq_change(tnl_conf_seq); } void -ovs_router_insert(ovs_be32 ip_dst, uint8_t plen, const char output_bridge[], - ovs_be32 gw) +ovs_router_insert(const struct in6_addr *ip_dst, uint8_t plen, + const char output_bridge[], const struct in6_addr *gw) { ovs_router_insert__(plen, ip_dst, plen, output_bridge, gw); } + static bool -rt_entry_delete(uint8_t priority, ovs_be32 ip_dst, uint8_t plen) +__rt_entry_delete(const struct cls_rule *cr) +{ + struct ovs_router_entry *p = ovs_router_entry_cast(cr); + + tnl_port_map_delete_ipdev(p->output_bridge); + /* Remove it. */ + cr = classifier_remove(&cls, cr); + if (cr) { + ovsrcu_postpone(rt_entry_free, ovs_router_entry_cast(cr)); + return true; + } + return false; +} + +static bool +rt_entry_delete(uint8_t priority, const struct in6_addr *ip6_dst, uint8_t plen) { const struct cls_rule *cr; struct cls_rule rule; struct match match; + bool res = false; - rt_init_match(&match, ip_dst, plen); + rt_init_match(&match, ip6_dst, plen); - cls_rule_init(&rule, &match, priority, CLS_MIN_VERSION); + cls_rule_init(&rule, &match, priority); /* Find the exact rule. */ - cr = classifier_find_rule_exactly(&cls, &rule); + cr = classifier_find_rule_exactly(&cls, &rule, CLS_MAX_VERSION); if (cr) { - /* Remove it. */ ovs_mutex_lock(&mutex); - cr = classifier_remove(&cls, cr); + res = __rt_entry_delete(cr); ovs_mutex_unlock(&mutex); + } + return res; +} - if (cr) { - ovsrcu_postpone(rt_entry_free, ovs_router_entry_cast(cr)); - return true; - } +static bool +scan_ipv6_route(const char *s, struct in6_addr *addr, unsigned int *plen) +{ + char *error = ipv6_parse_cidr(s, addr, plen); + if (error) { + free(error); + return false; } - return false; + return true; } static bool scan_ipv4_route(const char *s, ovs_be32 *addr, unsigned int *plen) { - int len, max_plen, n; - int slen = strlen(s); - uint8_t *ip = (uint8_t *)addr; - - *addr = htonl(0); - if (!ovs_scan(s, "%"SCNu8"%n", &ip[0], &n)) { + char *error = ip_parse_cidr(s, addr, plen); + if (error) { + free(error); return false; } - len = n; - max_plen = 8; - for (int i = 1; i < 4; i++) { - if (ovs_scan(s + len, ".%"SCNu8"%n", &ip[i], &n)) { - len += n; - max_plen += 8; - } else { - break; - } - } - if (len == slen && max_plen == 32) { - *plen = 32; - return true; - } - if (ovs_scan(s + len, "/%u%n", plen, &n) - && len + n == slen && *plen <= max_plen) { - return true; - } - return false; + return true; } static void ovs_router_add(struct unixctl_conn *conn, int argc, const char *argv[], void *aux OVS_UNUSED) { - ovs_be32 ip, gw; + ovs_be32 ip; unsigned int plen; + struct in6_addr ip6; + struct in6_addr gw6; if (scan_ipv4_route(argv[1], &ip, &plen)) { - if (argc > 3) { - inet_pton(AF_INET, argv[3], (struct in_addr *)&gw); - } else { - gw = 0; + ovs_be32 gw = 0; + if (argc > 3 && !ip_parse(argv[3], &gw)) { + unixctl_command_reply_error(conn, "Invalid gateway"); + return; + } + in6_addr_set_mapped_ipv4(&ip6, ip); + in6_addr_set_mapped_ipv4(&gw6, gw); + plen += 96; + } else if (scan_ipv6_route(argv[1], &ip6, &plen)) { + gw6 = in6addr_any; + if (argc > 3 && !ipv6_parse(argv[3], &gw6)) { + unixctl_command_reply_error(conn, "Invalid IPv6 gateway"); + return; } - ovs_router_insert__(plen + 32, ip, plen, argv[2], gw); - unixctl_command_reply(conn, "OK"); } else { - unixctl_command_reply(conn, "Invalid parameters"); + unixctl_command_reply_error(conn, "Invalid parameters"); + return; } + ovs_router_insert__(plen + 32, &ip6, plen, argv[2], &gw6); + unixctl_command_reply(conn, "OK"); } static void @@ -221,17 +256,20 @@ ovs_router_del(struct unixctl_conn *conn, int argc OVS_UNUSED, { ovs_be32 ip; unsigned int plen; + struct in6_addr ip6; if (scan_ipv4_route(argv[1], &ip, &plen)) { - - if (rt_entry_delete(plen + 32, ip, plen)) { - unixctl_command_reply(conn, "OK"); - seq_change(tnl_conf_seq); - } else { - unixctl_command_reply(conn, "Not found"); - } + in6_addr_set_mapped_ipv4(&ip6, ip); + plen += 96; + } else if (!scan_ipv6_route(argv[1], &ip6, &plen)) { + unixctl_command_reply_error(conn, "Invalid parameters"); + return; + } + if (rt_entry_delete(plen + 32, &ip6, plen)) { + unixctl_command_reply(conn, "OK"); + seq_change(tnl_conf_seq); } else { - unixctl_command_reply(conn, "Invalid parameters"); + unixctl_command_reply_error(conn, "Not found"); } } @@ -244,16 +282,21 @@ ovs_router_show(struct unixctl_conn *conn, int argc OVS_UNUSED, ds_put_format(&ds, "Route Table:\n"); CLS_FOR_EACH(rt, cr, &cls) { + uint8_t plen; if (rt->priority == rt->plen) { ds_put_format(&ds, "Cached: "); } else { ds_put_format(&ds, "User: "); } - ds_put_format(&ds, IP_FMT"/%"PRIu16" dev %s", - IP_ARGS(rt->nw_addr), rt->plen, - rt->output_bridge); - if (rt->gw) { - ds_put_format(&ds, " GW "IP_FMT, IP_ARGS(rt->gw)); + ipv6_format_mapped(&rt->nw_addr, &ds); + plen = rt->plen; + if (IN6_IS_ADDR_V4MAPPED(&rt->nw_addr)) { + plen -= 96; + } + ds_put_format(&ds, "/%"PRIu16" dev %s", plen, rt->output_bridge); + if (ipv6_addr_is_set(&rt->gw)) { + ds_put_format(&ds, " GW "); + ipv6_format_mapped(&rt->gw, &ds); } ds_put_format(&ds, "\n"); } @@ -266,23 +309,27 @@ ovs_router_lookup_cmd(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[], void *aux OVS_UNUSED) { ovs_be32 ip; + struct in6_addr ip6; unsigned int plen; + char iface[IFNAMSIZ]; + struct in6_addr gw; if (scan_ipv4_route(argv[1], &ip, &plen) && plen == 32) { - char iface[IFNAMSIZ]; - ovs_be32 gw; - - if (ovs_router_lookup(ip, iface, &gw)) { - struct ds ds = DS_EMPTY_INITIALIZER; + in6_addr_set_mapped_ipv4(&ip6, ip); + } else if (!(scan_ipv6_route(argv[1], &ip6, &plen) && plen == 128)) { + unixctl_command_reply_error(conn, "Invalid parameters"); + return; + } - ds_put_format(&ds, "gateway " IP_FMT "\n", IP_ARGS(gw)); - ds_put_format(&ds, "dev %s\n", iface); - unixctl_command_reply(conn, ds_cstr(&ds)); - } else { - unixctl_command_reply(conn, "Not found"); - } + if (ovs_router_lookup(&ip6, iface, &gw)) { + struct ds ds = DS_EMPTY_INITIALIZER; + ds_put_format(&ds, "gateway "); + ipv6_format_mapped(&ip6, &ds); + ds_put_format(&ds, "\ndev %s\n", iface); + unixctl_command_reply(conn, ds_cstr(&ds)); + ds_destroy(&ds); } else { - unixctl_command_reply(conn, "Invalid parameters"); + unixctl_command_reply_error(conn, "Not found"); } } @@ -295,9 +342,7 @@ ovs_router_flush(void) classifier_defer(&cls); CLS_FOR_EACH(rt, cr, &cls) { if (rt->priority == rt->plen) { - if (classifier_remove(&cls, &rt->cr)) { - ovsrcu_postpone(rt_entry_free, rt); - } + __rt_entry_delete(&rt->cr); } } classifier_publish(&cls); @@ -310,11 +355,11 @@ void ovs_router_init(void) { classifier_init(&cls, NULL); - unixctl_command_register("ovs/route/add", "ipv4_addr/prefix_len out_br_name gw", 2, 3, + unixctl_command_register("ovs/route/add", "ip_addr/prefix_len out_br_name gw", 2, 3, ovs_router_add, NULL); unixctl_command_register("ovs/route/show", "", 0, 0, ovs_router_show, NULL); - unixctl_command_register("ovs/route/del", "ipv4_addr/prefix_len", 1, 1, ovs_router_del, + unixctl_command_register("ovs/route/del", "ip_addr/prefix_len", 1, 1, ovs_router_del, NULL); - unixctl_command_register("ovs/route/lookup", "ipv4_addr", 1, 1, + unixctl_command_register("ovs/route/lookup", "ip_addr", 1, 1, ovs_router_lookup_cmd, NULL); }