LCOV - code coverage report
Current view: top level - lib - ovs-router.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 122 196 62.2 %
Date: 2016-09-14 01:02:56 Functions: 14 18 77.8 %
Branches: 50 104 48.1 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * Copyright (c) 2014, 2015, 2016 Nicira, Inc.
       3                 :            :  *
       4                 :            :  * Licensed under the Apache License, Version 2.0 (the "License");
       5                 :            :  * you may not use this file except in compliance with the License.
       6                 :            :  * You may obtain a copy of the License at:
       7                 :            :  *
       8                 :            :  *     http://www.apache.org/licenses/LICENSE-2.0
       9                 :            :  *
      10                 :            :  * Unless required by applicable law or agreed to in writing, software
      11                 :            :  * distributed under the License is distributed on an "AS IS" BASIS,
      12                 :            :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      13                 :            :  * See the License for the specific language governing permissions and
      14                 :            :  * limitations under the License.
      15                 :            :  */
      16                 :            : 
      17                 :            : #include <config.h>
      18                 :            : 
      19                 :            : #include "ovs-router.h"
      20                 :            : 
      21                 :            : #include <arpa/inet.h>
      22                 :            : #include <errno.h>
      23                 :            : #include <inttypes.h>
      24                 :            : #include <sys/socket.h>
      25                 :            : #include <net/if.h>
      26                 :            : #include <netinet/in.h>
      27                 :            : #include <stdarg.h>
      28                 :            : #include <stdlib.h>
      29                 :            : #include <string.h>
      30                 :            : #include <unistd.h>
      31                 :            : 
      32                 :            : #include "classifier.h"
      33                 :            : #include "command-line.h"
      34                 :            : #include "compiler.h"
      35                 :            : #include "dpif.h"
      36                 :            : #include "openvswitch/dynamic-string.h"
      37                 :            : #include "netdev.h"
      38                 :            : #include "packets.h"
      39                 :            : #include "seq.h"
      40                 :            : #include "ovs-thread.h"
      41                 :            : #include "route-table.h"
      42                 :            : #include "tnl-ports.h"
      43                 :            : #include "unixctl.h"
      44                 :            : #include "util.h"
      45                 :            : #include "unaligned.h"
      46                 :            : #include "unixctl.h"
      47                 :            : #include "util.h"
      48                 :            : 
      49                 :            : static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER;
      50                 :            : static struct classifier cls;
      51                 :            : 
      52                 :            : struct ovs_router_entry {
      53                 :            :     struct cls_rule cr;
      54                 :            :     char output_bridge[IFNAMSIZ];
      55                 :            :     struct in6_addr gw;
      56                 :            :     struct in6_addr nw_addr;
      57                 :            :     struct in6_addr src_addr;
      58                 :            :     uint8_t plen;
      59                 :            :     uint8_t priority;
      60                 :            : };
      61                 :            : 
      62                 :            : static struct ovs_router_entry *
      63                 :      90902 : ovs_router_entry_cast(const struct cls_rule *cr)
      64                 :            : {
      65                 :            :     if (offsetof(struct ovs_router_entry, cr) == 0) {
      66                 :      45451 :         return CONTAINER_OF(cr, struct ovs_router_entry, cr);
      67                 :            :     } else {
      68                 :            :         return cr ? CONTAINER_OF(cr, struct ovs_router_entry, cr) : NULL;
      69                 :            :     }
      70                 :            : }
      71                 :            : 
      72                 :            : static bool
      73                 :         14 : ovs_router_lookup_fallback(const struct in6_addr *ip6_dst, char output_bridge[],
      74                 :            :                            struct in6_addr *src6, struct in6_addr *gw6)
      75                 :            : {
      76                 :            :     ovs_be32 src;
      77                 :            : 
      78         [ +  - ]:         14 :     if (!route_table_fallback_lookup(ip6_dst, output_bridge, gw6)) {
      79                 :         14 :         return false;
      80                 :            :     }
      81         [ #  # ]:          0 :     if (netdev_get_in4_by_name(output_bridge, (struct in_addr *)&src)) {
      82                 :          0 :         return false;
      83                 :            :     }
      84         [ #  # ]:          0 :     if (src6) {
      85                 :          0 :         in6_addr_set_mapped_ipv4(src6, src);
      86                 :            :     }
      87                 :         14 :     return true;
      88                 :            : }
      89                 :            : 
      90                 :            : bool
      91                 :       6664 : ovs_router_lookup(const struct in6_addr *ip6_dst, char output_bridge[],
      92                 :            :                   struct in6_addr *src, struct in6_addr *gw)
      93                 :            : {
      94                 :            :     const struct cls_rule *cr;
      95                 :       6664 :     struct flow flow = {.ipv6_dst = *ip6_dst};
      96                 :            : 
      97                 :       6664 :     cr = classifier_lookup(&cls, OVS_VERSION_MAX, &flow, NULL);
      98         [ +  + ]:       6664 :     if (cr) {
      99                 :       6650 :         struct ovs_router_entry *p = ovs_router_entry_cast(cr);
     100                 :            : 
     101                 :       6650 :         ovs_strlcpy(output_bridge, p->output_bridge, IFNAMSIZ);
     102                 :       6650 :         *gw = p->gw;
     103         [ +  + ]:       6650 :         if (src) {
     104                 :       6252 :             *src = p->src_addr;
     105                 :            :         }
     106                 :       6650 :         return true;
     107                 :            :     }
     108                 :       6664 :     return ovs_router_lookup_fallback(ip6_dst, output_bridge, src, gw);
     109                 :            : }
     110                 :            : 
     111                 :            : static void
     112                 :      21978 : rt_entry_free(struct ovs_router_entry *p)
     113                 :            : {
     114                 :      21978 :     cls_rule_destroy(&p->cr);
     115                 :      21978 :     free(p);
     116                 :      21978 : }
     117                 :            : 
     118                 :      28476 : static void rt_init_match(struct match *match, const struct in6_addr *ip6_dst,
     119                 :            :                           uint8_t plen)
     120                 :            : {
     121                 :            :     struct in6_addr dst;
     122                 :            :     struct in6_addr mask;
     123                 :            : 
     124                 :      28476 :     mask = ipv6_create_mask(plen);
     125                 :            : 
     126                 :      28476 :     dst = ipv6_addr_bitand(ip6_dst, &mask);
     127                 :      28476 :     memset(match, 0, sizeof *match);
     128                 :      28476 :     match->flow.ipv6_dst = dst;
     129                 :      28476 :     match->wc.masks.ipv6_dst = mask;
     130                 :      28476 : }
     131                 :            : 
     132                 :            : static int
     133                 :      28476 : get_src_addr(const struct in6_addr *ip6_dst,
     134                 :            :              const char output_bridge[], struct in6_addr *psrc)
     135                 :            : {
     136                 :            :     struct in6_addr *mask, *addr6;
     137                 :      28476 :     int err, n_in6, i, max_plen = -1;
     138                 :            :     struct netdev *dev;
     139                 :            :     bool is_ipv4;
     140                 :            : 
     141                 :      28476 :     err = netdev_open(output_bridge, NULL, &dev);
     142         [ -  + ]:      28476 :     if (err) {
     143                 :          0 :         return err;
     144                 :            :     }
     145                 :            : 
     146                 :      28476 :     err = netdev_get_addr_list(dev, &addr6, &mask, &n_in6);
     147         [ +  + ]:      28476 :     if (err) {
     148                 :        738 :         goto out;
     149                 :            :     }
     150                 :            : 
     151 [ +  + ][ +  - ]:      27738 :     is_ipv4 = IN6_IS_ADDR_V4MAPPED(ip6_dst);
                 [ +  + ]
     152                 :            : 
     153         [ +  + ]:      78292 :     for (i = 0; i < n_in6; i++) {
     154                 :            :         struct in6_addr a1, a2;
     155                 :            :         int mask_bits;
     156                 :            : 
     157 [ +  + ][ +  + ]:      50554 :         if (is_ipv4 && !IN6_IS_ADDR_V4MAPPED(&addr6[i])) {
         [ +  - ][ +  + ]
                 [ +  + ]
     158                 :      12393 :             continue;
     159                 :            :         }
     160                 :            : 
     161                 :      38161 :         a1 = ipv6_addr_bitand(ip6_dst, &mask[i]);
     162                 :      38161 :         a2 = ipv6_addr_bitand(&addr6[i], &mask[i]);
     163                 :      38161 :         mask_bits = bitmap_count1(ALIGNED_CAST(const unsigned long *, &mask[i]), 128);
     164                 :            : 
     165 [ +  + ][ +  + ]:      38161 :         if (!memcmp(&a1, &a2, sizeof (a1)) && mask_bits > max_plen) {
     166                 :      27738 :             *psrc = addr6[i];
     167                 :      38161 :             max_plen = mask_bits;
     168                 :            :         }
     169                 :            :     }
     170         [ -  + ]:      27738 :     if (max_plen == -1) {
     171                 :          0 :         err = ENOENT;
     172                 :            :     }
     173                 :            : out:
     174                 :      28476 :     free(addr6);
     175                 :      28476 :     free(mask);
     176                 :      28476 :     netdev_close(dev);
     177                 :      28476 :     return err;
     178                 :            : }
     179                 :            : 
     180                 :            : static int
     181                 :      28476 : ovs_router_insert__(uint8_t priority, const struct in6_addr *ip6_dst,
     182                 :            :                     uint8_t plen, const char output_bridge[],
     183                 :            :                     const struct in6_addr *gw)
     184                 :            : {
     185                 :            :     const struct cls_rule *cr;
     186                 :            :     struct ovs_router_entry *p;
     187                 :            :     struct match match;
     188                 :            :     int err;
     189                 :            : 
     190                 :      28476 :     rt_init_match(&match, ip6_dst, plen);
     191                 :            : 
     192                 :      28476 :     p = xzalloc(sizeof *p);
     193                 :      28476 :     ovs_strlcpy(p->output_bridge, output_bridge, sizeof p->output_bridge);
     194         [ +  + ]:      28476 :     if (ipv6_addr_is_set(gw)) {
     195                 :       2532 :         p->gw = *gw;
     196                 :            :     }
     197                 :      28476 :     p->nw_addr = match.flow.ipv6_dst;
     198                 :      28476 :     p->plen = plen;
     199                 :      28476 :     p->priority = priority;
     200                 :      28476 :     err = get_src_addr(ip6_dst, output_bridge, &p->src_addr);
     201         [ +  + ]:      28476 :     if (err) {
     202                 :        738 :         free(p);
     203                 :        738 :         return err;
     204                 :            :     }
     205                 :            :     /* Longest prefix matches first. */
     206                 :      27738 :     cls_rule_init(&p->cr, &match, priority);
     207                 :            : 
     208                 :      27738 :     ovs_mutex_lock(&mutex);
     209                 :      27738 :     cr = classifier_replace(&cls, &p->cr, OVS_VERSION_MIN, NULL, 0);
     210                 :      27738 :     ovs_mutex_unlock(&mutex);
     211                 :            : 
     212         [ +  + ]:      27738 :     if (cr) {
     213                 :            :         /* An old rule with the same match was displaced. */
     214                 :       5239 :         ovsrcu_postpone(rt_entry_free, ovs_router_entry_cast(cr));
     215                 :            :     }
     216                 :      27738 :     tnl_port_map_insert_ipdev(output_bridge);
     217                 :      27738 :     seq_change(tnl_conf_seq);
     218                 :      28476 :     return 0;
     219                 :            : }
     220                 :            : 
     221                 :            : void
     222                 :      28431 : ovs_router_insert(const struct in6_addr *ip_dst, uint8_t plen,
     223                 :            :                   const char output_bridge[], const struct in6_addr *gw)
     224                 :            : {
     225                 :      28431 :     ovs_router_insert__(plen, ip_dst, plen, output_bridge, gw);
     226                 :      28431 : }
     227                 :            : 
     228                 :            : 
     229                 :            : static bool
     230                 :      16781 : __rt_entry_delete(const struct cls_rule *cr)
     231                 :            : {
     232                 :      16781 :     struct ovs_router_entry *p = ovs_router_entry_cast(cr);
     233                 :            : 
     234                 :      16781 :     tnl_port_map_delete_ipdev(p->output_bridge);
     235                 :            :     /* Remove it. */
     236                 :      16781 :     cr = classifier_remove(&cls, cr);
     237         [ +  - ]:      16781 :     if (cr) {
     238                 :      16781 :         ovsrcu_postpone(rt_entry_free, ovs_router_entry_cast(cr));
     239                 :      16781 :         return true;
     240                 :            :     }
     241                 :          0 :     return false;
     242                 :            : }
     243                 :            : 
     244                 :            : static bool
     245                 :          0 : rt_entry_delete(uint8_t priority, const struct in6_addr *ip6_dst, uint8_t plen)
     246                 :            : {
     247                 :            :     const struct cls_rule *cr;
     248                 :            :     struct cls_rule rule;
     249                 :            :     struct match match;
     250                 :          0 :     bool res = false;
     251                 :            : 
     252                 :          0 :     rt_init_match(&match, ip6_dst, plen);
     253                 :            : 
     254                 :          0 :     cls_rule_init(&rule, &match, priority);
     255                 :            : 
     256                 :            :     /* Find the exact rule. */
     257                 :          0 :     cr = classifier_find_rule_exactly(&cls, &rule, OVS_VERSION_MAX);
     258         [ #  # ]:          0 :     if (cr) {
     259                 :          0 :         ovs_mutex_lock(&mutex);
     260                 :          0 :         res = __rt_entry_delete(cr);
     261                 :          0 :         ovs_mutex_unlock(&mutex);
     262                 :            :     }
     263                 :          0 :     return res;
     264                 :            : }
     265                 :            : 
     266                 :            : static bool
     267                 :          1 : scan_ipv6_route(const char *s, struct in6_addr *addr, unsigned int *plen)
     268                 :            : {
     269                 :          1 :     char *error = ipv6_parse_cidr(s, addr, plen);
     270         [ -  + ]:          1 :     if (error) {
     271                 :          0 :         free(error);
     272                 :          0 :         return false;
     273                 :            :     }
     274                 :          1 :     return true;
     275                 :            : }
     276                 :            : 
     277                 :            : static bool
     278                 :         45 : scan_ipv4_route(const char *s, ovs_be32 *addr, unsigned int *plen)
     279                 :            : {
     280                 :         45 :     char *error = ip_parse_cidr(s, addr, plen);
     281         [ +  + ]:         45 :     if (error) {
     282                 :          1 :         free(error);
     283                 :          1 :         return false;
     284                 :            :     }
     285                 :         44 :     return true;
     286                 :            : }
     287                 :            : 
     288                 :            : static void
     289                 :         45 : ovs_router_add(struct unixctl_conn *conn, int argc,
     290                 :            :               const char *argv[], void *aux OVS_UNUSED)
     291                 :            : {
     292                 :            :     ovs_be32 ip;
     293                 :            :     unsigned int plen;
     294                 :            :     struct in6_addr ip6;
     295                 :            :     struct in6_addr gw6;
     296                 :            :     int err;
     297                 :            : 
     298         [ +  + ]:         45 :     if (scan_ipv4_route(argv[1], &ip, &plen)) {
     299                 :         44 :         ovs_be32 gw = 0;
     300 [ -  + ][ #  # ]:         44 :         if (argc > 3 && !ip_parse(argv[3], &gw)) {
     301                 :          0 :             unixctl_command_reply_error(conn, "Invalid gateway");
     302                 :          0 :             return;
     303                 :            :         }
     304                 :         44 :         in6_addr_set_mapped_ipv4(&ip6, ip);
     305                 :         44 :         in6_addr_set_mapped_ipv4(&gw6, gw);
     306                 :         44 :         plen += 96;
     307         [ +  - ]:          1 :     } else if (scan_ipv6_route(argv[1], &ip6, &plen)) {
     308                 :          1 :         gw6 = in6addr_any;
     309 [ -  + ][ #  # ]:          1 :         if (argc > 3 && !ipv6_parse(argv[3], &gw6)) {
     310                 :          0 :             unixctl_command_reply_error(conn, "Invalid IPv6 gateway");
     311                 :          0 :             return;
     312                 :            :         }
     313                 :            :     } else {
     314                 :          0 :         unixctl_command_reply_error(conn, "Invalid parameters");
     315                 :          0 :         return;
     316                 :            :     }
     317                 :         45 :     err = ovs_router_insert__(plen + 32, &ip6, plen, argv[2], &gw6);
     318         [ -  + ]:         45 :     if (err) {
     319                 :          0 :         unixctl_command_reply(conn, "Error while inserting route.");
     320                 :            :     } else {
     321                 :         45 :         unixctl_command_reply(conn, "OK");
     322                 :            :     }
     323                 :            : }
     324                 :            : 
     325                 :            : static void
     326                 :          0 : ovs_router_del(struct unixctl_conn *conn, int argc OVS_UNUSED,
     327                 :            :               const char *argv[], void *aux OVS_UNUSED)
     328                 :            : {
     329                 :            :     ovs_be32 ip;
     330                 :            :     unsigned int plen;
     331                 :            :     struct in6_addr ip6;
     332                 :            : 
     333         [ #  # ]:          0 :     if (scan_ipv4_route(argv[1], &ip, &plen)) {
     334                 :          0 :         in6_addr_set_mapped_ipv4(&ip6, ip);
     335                 :          0 :         plen += 96;
     336         [ #  # ]:          0 :     } else if (!scan_ipv6_route(argv[1], &ip6, &plen)) {
     337                 :          0 :         unixctl_command_reply_error(conn, "Invalid parameters");
     338                 :          0 :         return;
     339                 :            :     }
     340         [ #  # ]:          0 :     if (rt_entry_delete(plen + 32, &ip6, plen)) {
     341                 :          0 :         unixctl_command_reply(conn, "OK");
     342                 :          0 :         seq_change(tnl_conf_seq);
     343                 :            :     } else {
     344                 :          0 :         unixctl_command_reply_error(conn, "Not found");
     345                 :            :     }
     346                 :            : }
     347                 :            : 
     348                 :            : static void
     349                 :          0 : ovs_router_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
     350                 :            :                const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
     351                 :            : {
     352                 :            :     struct ovs_router_entry *rt;
     353                 :          0 :     struct ds ds = DS_EMPTY_INITIALIZER;
     354                 :            : 
     355                 :          0 :     ds_put_format(&ds, "Route Table:\n");
     356 [ #  # ][ #  # ]:          0 :     CLS_FOR_EACH(rt, cr, &cls) {
     357                 :            :         uint8_t plen;
     358         [ #  # ]:          0 :         if (rt->priority == rt->plen) {
     359                 :          0 :             ds_put_format(&ds, "Cached: ");
     360                 :            :         } else {
     361                 :          0 :             ds_put_format(&ds, "User: ");
     362                 :            :         }
     363                 :          0 :         ipv6_format_mapped(&rt->nw_addr, &ds);
     364                 :          0 :         plen = rt->plen;
     365 [ #  # ][ #  # ]:          0 :         if (IN6_IS_ADDR_V4MAPPED(&rt->nw_addr)) {
         [ #  # ][ #  # ]
     366                 :          0 :             plen -= 96;
     367                 :            :         }
     368                 :          0 :         ds_put_format(&ds, "/%"PRIu16" dev %s", plen, rt->output_bridge);
     369         [ #  # ]:          0 :         if (ipv6_addr_is_set(&rt->gw)) {
     370                 :          0 :             ds_put_format(&ds, " GW ");
     371                 :          0 :             ipv6_format_mapped(&rt->gw, &ds);
     372                 :            :         }
     373                 :          0 :         ds_put_format(&ds, " SRC ");
     374                 :          0 :         ipv6_format_mapped(&rt->src_addr, &ds);
     375                 :          0 :         ds_put_format(&ds, "\n");
     376                 :            :     }
     377                 :          0 :     unixctl_command_reply(conn, ds_cstr(&ds));
     378                 :          0 :     ds_destroy(&ds);
     379                 :          0 : }
     380                 :            : 
     381                 :            : static void
     382                 :          0 : ovs_router_lookup_cmd(struct unixctl_conn *conn, int argc OVS_UNUSED,
     383                 :            :                       const char *argv[], void *aux OVS_UNUSED)
     384                 :            : {
     385                 :            :     ovs_be32 ip;
     386                 :            :     struct in6_addr ip6;
     387                 :            :     unsigned int plen;
     388                 :            :     char iface[IFNAMSIZ];
     389                 :            :     struct in6_addr gw, src;
     390                 :            : 
     391 [ #  # ][ #  # ]:          0 :     if (scan_ipv4_route(argv[1], &ip, &plen) && plen == 32) {
     392                 :          0 :         in6_addr_set_mapped_ipv4(&ip6, ip);
     393 [ #  # ][ #  # ]:          0 :     } else if (!(scan_ipv6_route(argv[1], &ip6, &plen) && plen == 128)) {
     394                 :          0 :         unixctl_command_reply_error(conn, "Invalid parameters");
     395                 :          0 :         return;
     396                 :            :     }
     397                 :            : 
     398         [ #  # ]:          0 :     if (ovs_router_lookup(&ip6, iface, &src, &gw)) {
     399                 :          0 :         struct ds ds = DS_EMPTY_INITIALIZER;
     400                 :          0 :         ds_put_format(&ds, "src ");
     401                 :          0 :         ipv6_format_mapped(&src, &ds);
     402                 :          0 :         ds_put_format(&ds, "gateway ");
     403                 :          0 :         ipv6_format_mapped(&gw, &ds);
     404                 :          0 :         ds_put_format(&ds, "\ndev %s\n", iface);
     405                 :          0 :         unixctl_command_reply(conn, ds_cstr(&ds));
     406                 :          0 :         ds_destroy(&ds);
     407                 :            :     } else {
     408                 :          0 :         unixctl_command_reply_error(conn, "Not found");
     409                 :            :     }
     410                 :            : }
     411                 :            : 
     412                 :            : void
     413                 :       2488 : ovs_router_flush(void)
     414                 :            : {
     415                 :            :     struct ovs_router_entry *rt;
     416                 :            : 
     417                 :       2488 :     ovs_mutex_lock(&mutex);
     418                 :       2488 :     classifier_defer(&cls);
     419 [ +  + ][ +  + ]:      19269 :     CLS_FOR_EACH(rt, cr, &cls) {
     420         [ +  - ]:      16781 :         if (rt->priority == rt->plen) {
     421                 :      16781 :             __rt_entry_delete(&rt->cr);
     422                 :            :         }
     423                 :            :     }
     424                 :       2488 :     classifier_publish(&cls);
     425                 :       2488 :     ovs_mutex_unlock(&mutex);
     426                 :       2488 :     seq_change(tnl_conf_seq);
     427                 :       2488 : }
     428                 :            : 
     429                 :            : /* May not be called more than once. */
     430                 :            : void
     431                 :        684 : ovs_router_init(void)
     432                 :            : {
     433                 :        684 :     classifier_init(&cls, NULL);
     434                 :        684 :     unixctl_command_register("ovs/route/add", "ip_addr/prefix_len out_br_name gw", 2, 3,
     435                 :            :                              ovs_router_add, NULL);
     436                 :        684 :     unixctl_command_register("ovs/route/show", "", 0, 0, ovs_router_show, NULL);
     437                 :        684 :     unixctl_command_register("ovs/route/del", "ip_addr/prefix_len", 1, 1, ovs_router_del,
     438                 :            :                              NULL);
     439                 :        684 :     unixctl_command_register("ovs/route/lookup", "ip_addr", 1, 1,
     440                 :            :                              ovs_router_lookup_cmd, NULL);
     441                 :        684 : }

Generated by: LCOV version 1.12