Summary of this email:

1. I respond to a couple of specific points made by other folks in this
   thread to clarify what I'm trying to accomplish (set up a couple of
   ad hoc link-local routes without having to ask my ISP for a larger
   subnet) and to acknowledge that I said something stupid about pings.

2. I abandon my quest to get NDP proxying added to iked and instead ask
   if we can add a "rtlabel" keyword to iked.conf to make it easier for
   me to write a separate process that monitors the routing table to
   detect when the tunnel gets set up.

3. I ask three questions about the intended uses of routing labels, the
   purpose of iked's "cloned routes," and the viability of a routing
   socket that checks your privileges at the time it is opened instead
   of the time it is used.

4. I provide a first draft of a patch which adds that "rtlabel" keyword.


=== Part I. Responding to the discussion ===

Andy Bradford wrote:

>> I would also suggest comparing the "hackiness" of NDP proxying to the
>> hackiness of NAT, which is how we solve this same problem in IPv4.
>
> I realize I'm coming in late to this discussion, and may not actually
> have anything of value to add, but...
>
> I'm not sure how NDP proxying and NAT are related at all. I seems to
> me that NDP proxying is more akin to proxy ARP than NAT:
>
> http://man.openbsd.org/arp#s

They are related in that they are solutions to similar problems. In
IPv4 we use NAT to deal with the shortage of IP addresses. In IPv6 we
can use NDP proxying to deal with a local shortage of IPv6 subnets. My
purpose in writing the line you quoted was to argue that NDP proxying
is no more hacky than NAT, and the rest of the email tried to argue
that it was less hacky. Zack Newman, at least, disagrees, and nobody
jumped to defend my position. So OK, I will settle for calling them
equally hacky.

My goal in this discussion was to either convince someone that adding
automatic NDP proxying to iked was a good idea, or to at least get
agreement that it isn't a bad idea so that they would accept the code
if I wrote it myself. I failed in both of these objectives. But that's
why it matters to me whether NDP proxying is considered hacky or not.
If it's hacky, then the iked maintainers will reject a patch that adds
the ndp-proxy keyword even if I write it myself.


Zack Newman wrote:

> Yeah, I don't have the interest to get into it about this; but I find
> it (informally) inconsistent to take an ideological stance against NAT
> and not have a similar stance against NDP proxying.

To this I'll say that my stance against NAT isn't ideological. It's
just that NAT is more intrusive than NDP proxying. All NDP proxying
does is tell nearby hosts to update their routing tables to do exactly
what you want them to do. NAT, on the other hand, rewrites addresses
and ports so the packet you send out isn't the packet the other end
receives. And I'm not saying that people shouldn't use NAT for IPv4.
I just think that in the IPv6 case, if getting more subnets isn't an
easy affair, NDP proxying is a less-intrusive hack to get your VPN
client's traffic routed properly than NAT is, and as such, setting it
up ought to be as convenient and hassle-free as adding rules to pf.


> Also not sure where you heard that ICMP does not work with NAT. Surely
> you don't believe that. Go ahead and use ping(8) on any device that
> relies on NAT to talk to the outside world and witness how it
> "magically" works. ICMP uses the Query ID in lieu of a port number.

Yikes, I wasn't thinking clearly. While it's true that an external host
can't ping the NATted host (it can only ping the server which is doing
the NAT), that isn't the gist of what I claimed. Yes, you are correct.


> Will NDP proxying work? Depending on what you want, sure just like NAT
> will likely work. Relying on a simple routing table is far more ideal.
> NDP proxying is also vulnerable to NDP cache DoS. You can use your
> favorite search engine to learn why NDP proxying is not as good as
> simple routes.

Thanks for the specific example. I looked into this and it seems that
my use case might be misunderstood. The NDP cache DoS depends on a
setup similar to what the "nd-reflector" project (linked to in a
previous email in this thread) provides: NDP-proxying an entire subnet
instead of just the one host in question. Under these circumstances,
an attacker can fill the router's NDP cache with a bunch of
incorrectly-proxied addresses that don't actually point to anything,
just by trying to hit a bunch of random addresses in the subnet and
tricking the NDP proxy into responding to all of them.

The use case I have in mind is to only proxy an address when it is
allocated to a client that has connected to the VPN, and to stop
proxying that address when the client disconnects. That's why I thought
it was something iked ought to be able to do: when the client connects
to iked, iked knows what address it assigned the client so it can
automatically add this address to the router table with the RTF_ANNOUNCE
flag set (which is all that "ndp -s" does when I add it to the cache
manually). Since iked also knows when the client disconnects, it would
be an excellent candidate for removing the router table entry as well.
Thus at any moment, the router table on the IKEv2 responder is set to
NDP proxy only those addresses which correspond to connected clients
for which the "ndp-proxy" configuration flag was set in iked.conf. This
would not increase the risk of NDP cache DoS attacks any more than
physically plugging an additional host into the link-local network
would, because neighbor advertisements are only sent for neighbors that
actually exist (albeit across a tunnel).


=== Part II. My new approach ===

Since my proposal to have iked enable NDP proxying itself failed to
gain traction I looked into other options (that don't involve
requesting a larger subnet from my ISP and VPS providers). The
fundamental technical obstacle I have to overcome is allowing a
separate process to detect when clients connect and disconnect so that
it can add or remove the RTF_ANNOUNCE cache entry as appropriate.

Instead of tailing the iked log in /var/log/daemon, which seems awfully
brittle, my new idea is to open a routing socket and watch to see when
iked opens and closes the tunnel. (I'm learning about a lot of this as
I go so ideas that are obvious to you might not be obvious to me.) This
is still architecturally simple in the sense that my server process can
do it all with a single routing socket, which it uses both to detect
the incoming client and to add the NDP cache entry. So *now* my problem
narrows to figuring out which routing messages correspond to incoming
VPN clients that need to be proxied. I could hack it to look for
certain telltale signs of the changes I want (i.e. an individual
address from a particular subnet being added with a local destination
and particular flags set) but that still looks brittle so I have a
better plan involving routing labels.

My new idea still involves a change to iked, but I think this one will
be less controversial. I now propose the addition of the "rtlabel"
keyword to the iked.conf grammar:

ikev2 'server_config' passive esp \
        from any to dynamic \
        local 2001:db8:2::1 \
        srcid server.example.org \
        config address 2001:db8:2::/64 \
        tag "ROADW" \
        rtlabel "vpn_tunnel"

All this keyword does is cause ikev2 to add an RTA_LABEL with the value
"vpn_tunnel" to the RTM_ADD and RTM_DELETE updates that it performs
when setting up and tearing down this particular tunnel. This is no
more intrusive than the "tag" keyword that appears on the preceding
line---it just adds internal labels to messages that flow through the
kernel so that other processes can hook in and do whatever custom
behaviors they want. I've already verified that RTM_DELETE messages
can carry routing labels even though the label doesn't get added to the
table.

=== Part III. Some questions ===

I have a couple of questions about the routing labels and iked:

1. Right now slaacd automatically labels all the routes it creates with
"slaacd". As a result, I'm tempted to tag *all* of iked's routing
messages with "iked" by default (even routing messages that aren't
related to the tunnels) since the user already has to be aware that
some labels are taken. As an added bonus, a casual "route -v show" will
make it easier to figure out where all the routes are coming from.
Would this change have support if I did the legwork?

2. What is the purpose of iked's IMSG_VROUTE_CLONE? Apparently this
looks up the current route to the peer, replaces the destination field
(which I assume must be either the peer's address or a subnet
containing the peer's address) with the peer's address, and re-adds it
to the routing table. What benefit does this provide? I think I
understand "cloning" in the context of ARP and NDP caches with their
RTF_CLONING and RTF_CLONED flags, and I recognize the pattern here of
"take an existing route and re-add a more specific version of it"
(which isn't what "clone" means anywhere else in the world, but
whatever). But I don't understand why we would want to do it in this
context. Maybe it's a trick to make sure that encapsulated IPsec
packets don't get routed into the same tunnel a second time, in the
instance that the peer's address is contained in the subnet that is
supposed to be tunneled. But I don't see how that trick would work
for transport-mode IPsec, where the destination address is the same
before and after encapsulation---the cloned route seems to get the
same priority (IKED_VROUTE_PRIO) as the tunnel. So what does this
cloned route accomplish?

3. It's a common pattern in OpenBSD to "hoist" privileged operations to
program startup but it isn't possible to do this with routing sockets,
since the kernel checks your effective user ID at time of use rather
than at time of open. So here's an idea: what if we use the third
argument to socket(2) to pass a new "protocol family" PFWROUTE, so that
        socket(AF_ROUTE, SOCK_RAW, PF_WROUTE)
returns a routing socket that continues to work even after you've
dropped privileges or passed the socket to another process with sendfd?
I'm not confident of this, but I believe that the only reason slaacd's
parent process runs as root and not _slaacd is to allow it to make
routing table changes. My NDP proxy setter-upper process would have the
same issue: I can't drop privileges because I want to update the
routing table. Would this idea get any sort of traction?


=== Part 4. A tentative diff ===

Below is a rough draft of changes in /usr/src/sbin/iked that show what
I have in mind. The change to the iked.8 man page was just something
that I noticed in the code that wasn't documented. As for the rest of
the diff there are a number of things I'm doing wrong at the moment:

1. Search for "goto hack" to see how I currently deal with the fact
   that the IMSG_VROUTE_{ADD,CLONE,DELETE} messages don't have a nice
   bitfield like rtm_addrs. The correct approach is probably to add
   an addrs bitfield to the IMSG format.

2. The parent process doesn't remember the routing labels so when iked
   shuts down and the parent process calls vroute_cleanup, the labels
   are missing.

3. I don't have a solid plan yet on which routes would get whicho
   labels. In this draft the IMSG_VROUTE_CLONE route picks up the
   routing label corresponding to the tunnel, but I suspect that in the
   final draft, clone routes will get the default "iked" label. On the
   other hand, in the final draft RTM_NEWADDR and RTM_DELADDR messages
   that add and delete addresses on an interface probably *will* get
   the same policy-specific tag that the tunnnel routes get. There are
   other routing messages that I haven't even touched yet. Whether I
   continue with all the other routes depends on what the mailing list
   says about what's likely to be accepted. What matters to me is that
   I have an easy way to identify the route corresponding to a recently
   added tunnel. (The addition of an address to an interface doesn't
   matter to me because that happens on the client but the NDP proxying
   happens on the server. But I'm thinking it should get the same
   rtlabel in case someone else ends up having a use for it, since my
   impression is that route labels are essentially free.)

If I continued work in this direction, would it be likely to get
merged?

Thanks and regards,
Anthony Coulter




diff --git iked.8 iked.8
index 7134e54f843..8af88b529c8 100644
--- iked.8
+++ iked.8
@@ -110,7 +110,7 @@ negotiate NAT-Traversal with the peers.
 .It Fl V
 Show the version and exit.
 .It Fl v
-Produce more verbose output.
+Produce more verbose output. Repeating this argument increases the verbosity.
 .El
 .Sh PUBLIC KEY AUTHENTICATION
 It is possible to store trusted public keys to make them directly
diff --git iked.conf.5 iked.conf.5
index 5ca57e4767e..2189918329d 100644
--- iked.conf.5
+++ iked.conf.5
@@ -775,6 +775,13 @@ for filtering and monitoring.
 The traffic will be blocked if the specified
 .Ar interface
 does not exist.
+.Pp
+.It Ic rtlabel Ar string
+Set an
+.Dv RTA_LABEL
+of
+.Ar string
+in the routing socket messages that create and delete the tunneled routes.
 .El
 .Sh PACKET FILTERING
 IPsec traffic appears unencrypted on the
diff --git iked.h iked.h
index dc97f6560f9..91ad6f58653 100644
--- iked.h
+++ iked.h
@@ -21,6 +21,7 @@
 #include <sys/tree.h>
 #include <sys/queue.h>
 #include <arpa/inet.h>
+#include <net/route.h>
 #include <limits.h>
 #include <imsg.h>
 
@@ -33,6 +34,8 @@
 #define MINIMUM(a,b) (((a)<(b))?(a):(b))
 #define roundup(x, y)   ((((x)+((y)-1))/(y))*(y))
 
+#define IKED_RTLABEL "iked"
+
 #ifndef IKED_H
 #define IKED_H
 
@@ -301,6 +304,8 @@ struct iked_policy {
 
        struct iked_sapeers              pol_sapeers;
 
+       char                             pol_rtlabel[RTLABEL_LEN];
+
        TAILQ_ENTRY(iked_policy)         pol_entry;
 };
 TAILQ_HEAD(iked_policies, iked_policy);
@@ -1043,11 +1048,11 @@ int vroute_getaddr(struct iked *, struct imsg *);
 int vroute_setdns(struct iked *, int, struct sockaddr *, unsigned int);
 int vroute_getdns(struct iked *, struct imsg *);
 int vroute_setaddroute(struct iked *, uint8_t, struct sockaddr *,
-    uint8_t, struct sockaddr *);
+    uint8_t, struct sockaddr *, const char *);
 int vroute_setcloneroute(struct iked *, uint8_t, struct sockaddr *,
-    uint8_t, struct sockaddr *);
+    uint8_t, struct sockaddr *, const char *);
 int vroute_setdelroute(struct iked *, uint8_t, struct sockaddr *,
-    uint8_t, struct sockaddr *);
+    uint8_t, struct sockaddr *, const char *);
 int vroute_getroute(struct iked *, struct imsg *);
 int vroute_getcloneroute(struct iked *, struct imsg *);
 
diff --git parse.y parse.y
index 075981db320..3199da5e2f4 100644
--- parse.y
+++ parse.y
@@ -381,7 +381,7 @@ int                  create_ike(char *, int, struct 
ipsec_addr_wrap *,
                            uint8_t, char *, char *,
                            uint32_t, struct iked_lifetime *,
                            struct iked_auth *, struct ipsec_filters *,
-                           struct ipsec_addr_wrap *, char *);
+                           struct ipsec_addr_wrap *, char *, char *);
 int                     create_user(const char *, const char *);
 int                     get_id_type(char *);
 uint8_t                         x2i(unsigned char *);
@@ -445,10 +445,10 @@ typedef struct {
 %token VENDORID NOVENDORID
 %token TOLERATE MAXAGE DYNAMIC
 %token CERTPARTIALCHAIN
-%token REQUEST IFACE
+%token REQUEST IFACE RTLABEL
 %token <v.string>              STRING
 %token <v.number>              NUMBER
-%type  <v.string>              string
+%type  <v.string>              string rtlabel
 %type  <v.satype>              satype
 %type  <v.proto>               proto proto_list protoval
 %type  <v.hosts>               hosts hosts_list
@@ -550,16 +550,20 @@ user              : USER STRING STRING            {
 
 ikev2rule      : IKEV2 name ikeflags satype af proto rdomain hosts_list peers
                    ike_sas child_sas ids ikelifetime lifetime ikeauth ikecfg
-                   iface filters {
+                   iface filters rtlabel {
                        if (create_ike($2, $5, $6, $7, $8, &$9, $10, $11, $4,
                            $3, $12.srcid, $12.dstid, $13, &$14, &$15,
-                           $18, $16, $17) == -1) {
+                           $18, $16, $17, $19) == -1) {
                                yyerror("create_ike failed");
                                YYERROR;
                        }
                }
                ;
 
+rtlabel                : /* empty */                   { $$ = NULL; }
+               | RTLABEL STRING                { $$ = $2; }
+               ;
+
 ikecfg         : /* empty */                   { $$ = NULL; }
                | ikecfgvals                    { $$ = $1; }
                ;
@@ -1390,6 +1394,7 @@ lookup(char *s)
                { "quick",              QUICK },
                { "rdomain",            RDOMAIN },
                { "request",            REQUEST },
+               { "rtlabel",            RTLABEL },
                { "sa",                 SA },
                { "set",                SET },
                { "skip",               SKIP },
@@ -2471,7 +2476,7 @@ create_ike(char *name, int af, struct ipsec_addr_wrap 
*ipproto,
     uint8_t flags, char *srcid, char *dstid,
     uint32_t ikelifetime, struct iked_lifetime *lt,
     struct iked_auth *authtype, struct ipsec_filters *filter,
-    struct ipsec_addr_wrap *ikecfg, char *iface)
+    struct ipsec_addr_wrap *ikecfg, char *iface, char *rtlabel)
 {
        char                     idstr[IKED_ID_SIZE];
        struct ipsec_addr_wrap  *ipa, *ipb, *ipp;
@@ -2897,6 +2902,18 @@ create_ike(char *name, int af, struct ipsec_addr_wrap 
*ipproto,
                break;
        }
 
+       bzero(pol.pol_rtlabel, sizeof pol.pol_rtlabel);
+       if (rtlabel) {
+               if (strlcpy(pol.pol_rtlabel, rtlabel, RTLABEL_LEN)
+                   >= RTLABEL_LEN) {
+                       yyerror("rtlabel \"%s\" too long; max length"
+                           "is %d\n", rtlabel, RTLABEL_LEN - 1);
+                       goto done;
+               }
+       } else {
+               strlcpy(pol.pol_rtlabel, IKED_RTLABEL, RTLABEL_LEN);
+       }
+
        log_debug("%s: using %s for peer %s", __func__,
            print_xf(ikeauth->auth_method, 0, methodxfs), idstr);
 
@@ -2957,6 +2974,7 @@ done:
        free(name);
        free(srcid);
        free(dstid);
+       free(rtlabel);
        return (ret);
 }
 
diff --git policy.c policy.c
index 0048b4a9281..5eafb8a3109 100644
--- policy.c
+++ policy.c
@@ -722,12 +722,13 @@ sa_configure_iface(struct iked *env, struct iked_sa *sa, 
int add)
        if (add) {
                /* Add direct route to peer */
                if (vroute_setcloneroute(env, getrtable(),
-                   (struct sockaddr *)&sa->sa_peer.addr, 0, NULL))
+                   (struct sockaddr *)&sa->sa_peer.addr, 0, NULL,
+                   sa->sa_policy->pol_rtlabel))
                        return (-1);
        } else {
                if (vroute_setdelroute(env, getrtable(),
-                   (struct sockaddr *)&sa->sa_peer.addr,
-                   0, NULL))
+                   (struct sockaddr *)&sa->sa_peer.addr, 0, NULL,
+                   sa->sa_policy->pol_rtlabel))
                        return (-1);
        }
 
@@ -752,12 +753,14 @@ sa_configure_iface(struct iked *env, struct iked_sa *sa, 
int add)
                if (add) {
                        if (vroute_setaddroute(env, rdomain,
                            (struct sockaddr *)&saflow->flow_dst.addr,
-                           saflow->flow_dst.addr_mask, caddr))
+                           saflow->flow_dst.addr_mask, caddr,
+                           sa->sa_policy->pol_rtlabel))
                                return (-1);
                } else {
                        if (vroute_setdelroute(env, rdomain,
                            (struct sockaddr *)&saflow->flow_dst.addr,
-                           saflow->flow_dst.addr_mask, caddr))
+                           saflow->flow_dst.addr_mask, caddr,
+                           sa->sa_policy->pol_rtlabel))
                                return (-1);
                }
        }
diff --git print.c print.c
index 5444f950bff..ba1b6d1ae86 100644
--- print.c
+++ print.c
@@ -25,6 +25,7 @@
 #include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 #include <unistd.h>
 #include <event.h>
 
@@ -239,5 +240,8 @@ print_policy(struct iked_policy *pol)
        if (pol->pol_tap != 0)
                print_verbose(" tap \"enc%u\"", pol->pol_tap);
 
+       if (strcmp(pol->pol_rtlabel, IKED_RTLABEL) != 0)
+               print_verbose(" rtlabel \"%s\"", pol->pol_rtlabel);
+
        print_verbose("\n");
 }
diff --git vroute.c vroute.c
index 5ef892d61ab..c750d1c469b 100644
--- vroute.c
+++ vroute.c
@@ -41,9 +41,9 @@
 #define ROUNDUP(a) (a>0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : 
sizeof(long))
 
 int vroute_setroute(struct iked *, uint32_t, struct sockaddr *, uint8_t,
-    struct sockaddr *, int);
+    struct sockaddr *, const char *, int);
 int vroute_doroute(struct iked *, int, int, int, uint8_t, struct sockaddr *,
-    struct sockaddr *, struct sockaddr *, int *);
+    struct sockaddr *, struct sockaddr *, struct sockaddr *, int *need_gw);
 int vroute_doaddr(struct iked *, char *, struct sockaddr *, struct sockaddr *, 
int);
 int vroute_dodns(struct iked *, struct sockaddr *, int, unsigned int);
 void vroute_cleanup(struct iked *);
@@ -210,7 +210,7 @@ vroute_cleanup(struct iked *env)
                    route->vr_flags, route->vr_rdomain, RTM_DELETE,
                    (struct sockaddr *)&route->vr_dest,
                    (struct sockaddr *)&route->vr_mask,
-                   NULL, NULL);
+                   NULL, NULL, NULL);
                TAILQ_REMOVE(&ivr->ivr_routes, route, vr_entry);
                free(route);
        }
@@ -493,36 +493,37 @@ vroute_removeaddr(struct iked *env, int ifidx, struct 
sockaddr *addr,
 
 int
 vroute_setaddroute(struct iked *env, uint8_t rdomain, struct sockaddr *dst,
-    uint8_t mask, struct sockaddr *ifa)
+    uint8_t mask, struct sockaddr *ifa, const char *rtlabel)
 {
-       return (vroute_setroute(env, rdomain, dst, mask, ifa,
+       return (vroute_setroute(env, rdomain, dst, mask, ifa, rtlabel,
            IMSG_VROUTE_ADD));
 }
 
 int
 vroute_setcloneroute(struct iked *env, uint8_t rdomain, struct sockaddr *dst,
-    uint8_t mask, struct sockaddr *addr)
+    uint8_t mask, struct sockaddr *addr, const char *rtlabel)
 {
-       return (vroute_setroute(env, rdomain, dst, mask, addr,
+       return (vroute_setroute(env, rdomain, dst, mask, addr, rtlabel,
            IMSG_VROUTE_CLONE));
 }
 
 int
 vroute_setdelroute(struct iked *env, uint8_t rdomain, struct sockaddr *dst,
-    uint8_t mask, struct sockaddr *addr)
+    uint8_t mask, struct sockaddr *addr, const char *rtlabel)
 {
-       return (vroute_setroute(env, rdomain, dst, mask, addr,
+       return (vroute_setroute(env, rdomain, dst, mask, addr, rtlabel,
            IMSG_VROUTE_DEL));
 }
 
 int
 vroute_setroute(struct iked *env, uint32_t rdomain, struct sockaddr *dst,
-    uint8_t mask, struct sockaddr *addr, int type)
+    uint8_t mask, struct sockaddr *addr, const char *rtlabel, int type)
 {
        struct sockaddr_storage  sa;
+       struct sockaddr_rtlabel  sr;
        struct sockaddr_in      *in;
        struct sockaddr_in6     *in6;
-       struct iovec             iov[5];
+       struct iovec             iov[6];
        int                      iovcnt = 0;
        uint8_t                  af;
 
@@ -567,6 +568,14 @@ vroute_setroute(struct iked *env, uint32_t rdomain, struct 
sockaddr *dst,
                iovcnt++;
        }
 
+       bzero(&sr, sizeof sr);
+       sr.sr_len = sizeof sr;
+       sr.sr_family = AF_UNSPEC;
+       strlcpy(sr.sr_label, rtlabel, sizeof sr.sr_label);
+       iov[iovcnt].iov_base = &sr;
+       iov[iovcnt].iov_len = sr.sr_len;
+       iovcnt++;
+
        return (proc_composev(&env->sc_ps, PROC_PARENT, type, iov, iovcnt));
 }
 
@@ -574,6 +583,7 @@ int
 vroute_getroute(struct iked *env, struct imsg *imsg)
 {
        struct sockaddr         *dest, *mask = NULL, *gateway = NULL;
+       struct sockaddr         *rtlabel = NULL;
        uint8_t                 *ptr;
        size_t                   left;
        int                      addrs = 0;
@@ -600,9 +610,11 @@ vroute_getroute(struct iked *env, struct imsg *imsg)
        addrs |= RTA_DST;
 
        flags = RTF_UP | RTF_GATEWAY | RTF_STATIC;
-       if (left != 0) {
+       if (left != 0 && left) {
                if (left < sizeof(struct sockaddr))
                        return (-1);
+               if ((struct sockaddr *)ptr == AF_UNSPEC)
+                       goto hack; /* Not a netmask! */
                mask = (struct sockaddr *)ptr;
                if (left < mask->sa_len)
                        return (-1);
@@ -624,6 +636,16 @@ vroute_getroute(struct iked *env, struct imsg *imsg)
                flags |= RTF_HOST;
        }
 
+ hack:
+       if (left < sizeof(struct sockaddr))
+               return (-1);
+       rtlabel = (struct sockaddr *)ptr;
+       if (left < rtlabel->sa_len)
+               return (-1);
+       ptr += rtlabel->sa_len;
+       left -= rtlabel->sa_len;
+       addrs |= RTA_LABEL;
+
        switch(imsg->hdr.type) {
        case IMSG_VROUTE_ADD:
                type = RTM_ADD;
@@ -638,7 +660,7 @@ vroute_getroute(struct iked *env, struct imsg *imsg)
        else
                vroute_removeroute(env, rdomain, dest, mask);
        return (vroute_doroute(env, flags, addrs, rdomain, type,
-           dest, mask, gateway, NULL));
+           dest, mask, gateway, rtlabel, NULL));
 }
 
 int
@@ -648,6 +670,7 @@ vroute_getcloneroute(struct iked *env, struct imsg *imsg)
        struct sockaddr_storage  dest;
        struct sockaddr_storage  mask;
        struct sockaddr_storage  addr;
+       struct sockaddr_storage  rtlabel;
        uint8_t                 *ptr;
        size_t                   left;
        uint32_t                 rdomain;
@@ -667,6 +690,7 @@ vroute_getcloneroute(struct iked *env, struct imsg *imsg)
        bzero(&dest, sizeof(dest));
        bzero(&mask, sizeof(mask));
        bzero(&addr, sizeof(addr));
+       bzero(&rtlabel, sizeof(rtlabel));
 
        if (left < sizeof(struct sockaddr))
                return (-1);
@@ -677,11 +701,20 @@ vroute_getcloneroute(struct iked *env, struct imsg *imsg)
        ptr += dst->sa_len;
        left -= dst->sa_len;
 
+       if (left < sizeof(struct sockaddr_rtlabel))
+               return (-1);
+       dst = (struct sockaddr *)ptr;
+       if (left < dst->sa_len)
+               return (-1);
+       memcpy(&rtlabel, dst, dst->sa_len);
+       ptr += dst->sa_len;
+       left -= dst->sa_len;
+
        /* Get route to peer */
        flags = RTF_UP | RTF_HOST | RTF_STATIC;
        if (vroute_doroute(env, flags, RTA_DST, rdomain, RTM_GET,
            (struct sockaddr *)&dest, (struct sockaddr *)&mask,
-           (struct sockaddr *)&addr, &need_gw))
+           (struct sockaddr *)&addr, NULL, &need_gw))
                return (-1);
 
        if (need_gw)
@@ -692,10 +725,11 @@ vroute_getcloneroute(struct iked *env, struct imsg *imsg)
        vroute_insertroute(env, rdomain, (struct sockaddr *)&dest, NULL);
 
        /* Set explicit route to peer with gateway addr*/
-       addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
+       addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK | RTA_LABEL;
        return (vroute_doroute(env, flags, addrs, rdomain, RTM_ADD,
            (struct sockaddr *)&dest, (struct sockaddr *)&mask,
-           (struct sockaddr *)&addr, NULL));
+           (struct sockaddr *)&addr, (struct sockaddr *)&rtlabel,
+           NULL));
 }
 
 int
@@ -767,10 +801,11 @@ vroute_dodns(struct iked *env, struct sockaddr *dns, int 
add,
 
 int
 vroute_doroute(struct iked *env, int flags, int addrs, int rdomain, uint8_t 
type,
-    struct sockaddr *dest, struct sockaddr *mask, struct sockaddr *addr, int 
*need_gw)
+    struct sockaddr *dest, struct sockaddr *mask, struct sockaddr *addr,
+    struct sockaddr *rtlabel, int *need_gw)
 {
        struct vroute_msg        m_rtmsg;
-       struct iovec             iov[7];
+       struct iovec             iov[9];
        struct iked_vroute_sc   *ivr = env->sc_vroute;
        ssize_t                  len;
        int                      iovcnt = 0;
@@ -829,11 +864,24 @@ vroute_doroute(struct iked *env, int flags, int addrs, 
int rdomain, uint8_t type
                }
        }
 
+       if (rtm.rtm_addrs & RTA_LABEL) {
+               iov[iovcnt].iov_base = rtlabel;
+               iov[iovcnt].iov_len = rtlabel->sa_len;
+               iovcnt++;
+               padlen = ROUNDUP(rtlabel->sa_len) - rtlabel->sa_len;
+               if (padlen > 0) {
+                       iov[iovcnt].iov_base = &pad;
+                       iov[iovcnt].iov_len = padlen;
+                       iovcnt++;
+               }
+       }
+
        for (i = 0; i < iovcnt; i++)
                rtm.rtm_msglen += iov[i].iov_len;
 
        log_debug("%s: len: %u type: %s rdomain: %d flags %x (%s%s)"
-           " addrs %x (dst %s mask %s gw %s)", __func__, rtm.rtm_msglen,
+           " addrs %x (dst %s mask %s gw %s) %s%s",
+           __func__, rtm.rtm_msglen,
            type == RTM_ADD ? "RTM_ADD" : type == RTM_DELETE ? "RTM_DELETE" :
            type == RTM_GET ? "RTM_GET" : "unknown", rdomain,
            flags,
@@ -842,7 +890,9 @@ vroute_doroute(struct iked *env, int flags, int addrs, int 
rdomain, uint8_t type
            addrs,
            addrs & RTA_DST ? print_addr(dest) : "<>",
            addrs & RTA_NETMASK ? print_addr(mask) : "<>",
-           addrs & RTA_GATEWAY ? print_addr(addr) : "<>");
+           addrs & RTA_GATEWAY ? print_addr(addr) : "<>",
+           addrs & RTA_LABEL ? "label " : "",
+           addrs & RTA_LABEL ? ((struct sockaddr_rtlabel*)rtlabel)->sr_label : 
"");
 
        if (writev(ivr->ivr_rtsock, iov, iovcnt) == -1) {
                if ((type == RTM_ADD && errno != EEXIST) ||

Reply via email to