chiark / gitweb /
resolver: construct comm_addr; honour multiple addresses from the resolver
[secnet.git] / netlink.c
index 787f4eb..e197e80 100644 (file)
--- a/netlink.c
+++ b/netlink.c
@@ -180,7 +180,7 @@ static inline uint16_t ip_fast_csum(const uint8_t *iph, int32_t ihl) {
     return sum;
 }
 #else
-static inline uint16_t ip_fast_csum(uint8_t *iph, int32_t ihl)
+static inline uint16_t ip_fast_csum(const uint8_t *iph, int32_t ihl)
 {
     assert(ihl < INT_MAX/4);
     return ip_csum(iph,ihl*4);
@@ -237,6 +237,20 @@ struct icmphdr {
 
 static const union icmpinfofield icmp_noinfo;
     
+static void netlink_client_deliver(struct netlink *st,
+                                  struct netlink_client *client,
+                                  uint32_t source, uint32_t dest,
+                                  struct buffer_if *buf);
+static void netlink_host_deliver(struct netlink *st,
+                                struct netlink_client *sender,
+                                uint32_t source, uint32_t dest,
+                                struct buffer_if *buf);
+
+static const char *sender_name(struct netlink_client *sender /* or NULL */)
+{
+    return sender?sender->name:"(local)";
+}
+
 static void netlink_packet_deliver(struct netlink *st,
                                   struct netlink_client *client,
                                   struct buffer_if *buf);
@@ -249,7 +263,8 @@ static void netlink_packet_deliver(struct netlink *st,
    settable.
    */
 static struct icmphdr *netlink_icmp_tmpl(struct netlink *st,
-                                        uint32_t dest,uint16_t len)
+                                        uint32_t source, uint32_t dest,
+                                        uint16_t len)
 {
     struct icmphdr *h;
 
@@ -265,7 +280,7 @@ static struct icmphdr *netlink_icmp_tmpl(struct netlink *st,
     h->iph.frag=0;
     h->iph.ttl=255; /* XXX should be configurable */
     h->iph.protocol=1;
-    h->iph.saddr=htonl(st->secnet_address);
+    h->iph.saddr=htonl(source);
     h->iph.daddr=htonl(dest);
     h->iph.check=0;
     h->iph.check=ip_fast_csum((uint8_t *)&h->iph,h->iph.ihl);
@@ -376,7 +391,9 @@ static uint16_t netlink_icmp_reply_len(struct buffer_if *buf)
 
 /* client indicates where the packet we're constructing a response to
    comes from. NULL indicates the host. */
-static void netlink_icmp_simple(struct netlink *st, struct buffer_if *buf,
+static void netlink_icmp_simple(struct netlink *st,
+                               struct netlink_client *origsender,
+                               struct buffer_if *buf,
                                uint8_t type, uint8_t code,
                                union icmpinfofield info)
 {
@@ -385,12 +402,44 @@ static void netlink_icmp_simple(struct netlink *st, struct buffer_if *buf,
 
     if (netlink_icmp_may_reply(buf)) {
        struct iphdr *iph=(struct iphdr *)buf->start;
+
+       uint32_t icmpdest = ntohl(iph->saddr);
+       uint32_t icmpsource;
+       const char *icmpsourcedebugprefix;
+       if (!st->ptp) {
+           icmpsource=st->secnet_address;
+           icmpsourcedebugprefix="";
+       } else if (origsender) {
+           /* was from peer, send reply as if from host */
+           icmpsource=st->local_address;
+           icmpsourcedebugprefix="L!";
+       } else {
+           /* was from host, send reply as if from peer */
+           icmpsource=st->secnet_address; /* actually, peer address */
+           icmpsourcedebugprefix="P!";
+       }
+       MDEBUG("%s: generating ICMP re %s[%s]->[%s]:"
+              " from %s%s type=%u code=%u\n",
+              st->name, sender_name(origsender),
+              ipaddr_to_string(ntohl(iph->saddr)),
+              ipaddr_to_string(ntohl(iph->daddr)),
+              icmpsourcedebugprefix,
+              ipaddr_to_string(icmpsource),
+              type, code);
+
        len=netlink_icmp_reply_len(buf);
-       h=netlink_icmp_tmpl(st,ntohl(iph->saddr),len);
+       h=netlink_icmp_tmpl(st,icmpsource,icmpdest,len);
        h->type=type; h->code=code; h->d=info;
-       memcpy(buf_append(&st->icmp,len),buf->start,len);
+       BUF_ADD_BYTES(append,&st->icmp,buf->start,len);
        netlink_icmp_csum(h);
-       netlink_packet_deliver(st,NULL,&st->icmp);
+
+       if (!st->ptp) {
+           netlink_packet_deliver(st,NULL,&st->icmp);
+       } else if (origsender) {
+           netlink_client_deliver(st,origsender,icmpsource,icmpdest,&st->icmp);
+       } else {
+           netlink_host_deliver(st,NULL,icmpsource,icmpdest,&st->icmp);
+       }
        BUF_ASSERT_FREE(&st->icmp);
     }
 }
@@ -465,6 +514,7 @@ static const char *fragment_filter_header(uint8_t *base, long *hlp)
 
 /* Fragment or send ICMP Fragmentation Needed */
 static void netlink_maybe_fragment(struct netlink *st,
+                                  struct netlink_client *sender,
                                   netlink_deliver_fn *deliver,
                                   void *deliver_dst,
                                   const char *delivery_name,
@@ -496,7 +546,7 @@ static void netlink_maybe_fragment(struct netlink *st,
     if (orig_frag&IPHDR_FRAG_DONT) {
        union icmpinfofield info =
            { .fragneeded = { .unused = 0, .mtu = htons(mtu) } };
-       netlink_icmp_simple(st,buf,
+       netlink_icmp_simple(st,sender,buf,
                            ICMP_TYPE_UNREACHABLE,
                            ICMP_CODE_FRAGMENTATION_REQUIRED,
                            info);
@@ -541,7 +591,7 @@ static void netlink_maybe_fragment(struct netlink *st,
        long avail = mtu - hl;
        long remain = endindata - indata;
        long use = avail < remain ? (avail & ~(long)7) : remain;
-       memcpy(buf_append(buf, use), indata, use);
+       BUF_ADD_BYTES(append, buf, indata, use);
        indata += use;
 
        _Bool last_frag = indata >= endindata;
@@ -587,11 +637,10 @@ static void netlink_client_deliver(struct netlink *st,
        d=ipaddr_to_string(dest);
        Message(M_ERR,"%s: dropping %s->%s, client not registered\n",
                st->name,s,d);
-       free(s); free(d);
        BUF_FREE(buf);
        return;
     }
-    netlink_maybe_fragment(st, client->deliver,client->dst,client->name,
+    netlink_maybe_fragment(st,NULL, client->deliver,client->dst,client->name,
                           client->mtu, source,dest,buf);
     client->outcount++;
 }
@@ -599,24 +648,24 @@ static void netlink_client_deliver(struct netlink *st,
 /* Deliver a packet to the host; used after we have decided that that
  * is what to do with it. */
 static void netlink_host_deliver(struct netlink *st,
+                                struct netlink_client *sender,
                                 uint32_t source, uint32_t dest,
                                 struct buffer_if *buf)
 {
-    netlink_maybe_fragment(st, st->deliver_to_host,st->dst,"(host)",
+    netlink_maybe_fragment(st,sender, st->deliver_to_host,st->dst,"(host)",
                           st->mtu, source,dest,buf);
     st->outcount++;
 }
 
-/* Deliver a packet. "client" is the _origin_ of the packet, not its
-   destination, and is NULL for packets from the host and packets
+/* Deliver a packet. "sender"==NULL for packets from the host and packets
    generated internally in secnet.  */
 static void netlink_packet_deliver(struct netlink *st,
-                                  struct netlink_client *client,
+                                  struct netlink_client *sender,
                                   struct buffer_if *buf)
 {
     if (buf->size < (int)sizeof(struct iphdr)) {
        Message(M_ERR,"%s: trying to deliver a too-short packet"
-               " from %s!\n",st->name, client?client->name:"(local)");
+               " from %s!\n",st->name, sender_name(sender));
        BUF_FREE(buf);
        return;
     }
@@ -638,9 +687,9 @@ static void netlink_packet_deliver(struct netlink *st,
        return;
     }
     
-    /* Packets from the host (client==NULL) may always be routed.  Packets
+    /* Packets from the host (sender==NULL) may always be routed.  Packets
        from clients with the allow_route option will also be routed. */
-    if (!client || (client && (client->options & OPT_ALLOWROUTE)))
+    if (!sender || (sender && (sender->options & OPT_ALLOWROUTE)))
        allow_route=True;
 
     /* If !allow_route, we check the routing table anyway, and if
@@ -686,7 +735,7 @@ static void netlink_packet_deliver(struct netlink *st,
        /* The packet's not going down a tunnel.  It might (ought to)
           be for the host.   */
        if (ipset_contains_addr(st->networks,dest)) {
-           netlink_host_deliver(st,source,dest,buf);
+           netlink_host_deliver(st,sender,source,dest,buf);
            BUF_ASSERT_FREE(buf);
        } else {
            string_t s,d;
@@ -694,8 +743,7 @@ static void netlink_packet_deliver(struct netlink *st,
            d=ipaddr_to_string(dest);
            Message(M_DEBUG,"%s: don't know where to deliver packet "
                    "(s=%s, d=%s)\n", st->name, s, d);
-           free(s); free(d);
-           netlink_icmp_simple(st,buf,ICMP_TYPE_UNREACHABLE,
+           netlink_icmp_simple(st,sender,buf,ICMP_TYPE_UNREACHABLE,
                                ICMP_CODE_NET_UNREACHABLE, icmp_noinfo);
            BUF_FREE(buf);
        }
@@ -710,9 +758,8 @@ static void netlink_packet_deliver(struct netlink *st,
               with destination network administratively prohibited */
            Message(M_NOTICE,"%s: denied forwarding for packet (s=%s, d=%s)\n",
                    st->name,s,d);
-           free(s); free(d);
                    
-           netlink_icmp_simple(st,buf,ICMP_TYPE_UNREACHABLE,
+           netlink_icmp_simple(st,sender,buf,ICMP_TYPE_UNREACHABLE,
                                ICMP_CODE_NET_PROHIBITED, icmp_noinfo);
            BUF_FREE(buf);
        } else {
@@ -722,7 +769,7 @@ static void netlink_packet_deliver(struct netlink *st,
                BUF_ASSERT_FREE(buf);
            } else {
                /* Generate ICMP destination unreachable */
-               netlink_icmp_simple(st,buf,
+               netlink_icmp_simple(st,sender,buf,
                                    ICMP_TYPE_UNREACHABLE,
                                    ICMP_CODE_NET_UNREACHABLE,
                                    icmp_noinfo);
@@ -734,7 +781,7 @@ static void netlink_packet_deliver(struct netlink *st,
 }
 
 static void netlink_packet_forward(struct netlink *st, 
-                                  struct netlink_client *client,
+                                  struct netlink_client *sender,
                                   struct buffer_if *buf)
 {
     if (buf->size < (int)sizeof(struct iphdr)) return;
@@ -745,7 +792,7 @@ static void netlink_packet_forward(struct netlink *st,
     /* Packet has already been checked */
     if (iph->ttl<=1) {
        /* Generate ICMP time exceeded */
-       netlink_icmp_simple(st,buf,ICMP_TYPE_TIME_EXCEEDED,
+       netlink_icmp_simple(st,sender,buf,ICMP_TYPE_TIME_EXCEEDED,
                            ICMP_CODE_TTL_EXCEEDED,icmp_noinfo);
        BUF_FREE(buf);
        return;
@@ -754,13 +801,13 @@ static void netlink_packet_forward(struct netlink *st,
     iph->check=0;
     iph->check=ip_fast_csum((uint8_t *)iph,iph->ihl);
 
-    netlink_packet_deliver(st,client,buf);
+    netlink_packet_deliver(st,sender,buf);
     BUF_ASSERT_FREE(buf);
 }
 
 /* Deal with packets addressed explicitly to us */
 static void netlink_packet_local(struct netlink *st,
-                                struct netlink_client *client,
+                                struct netlink_client *sender,
                                 struct buffer_if *buf)
 {
     struct icmphdr *h;
@@ -803,7 +850,7 @@ static void netlink_packet_local(struct netlink *st,
        Message(M_WARNING,"%s: unknown incoming ICMP\n",st->name);
     } else {
        /* Send ICMP protocol unreachable */
-       netlink_icmp_simple(st,buf,ICMP_TYPE_UNREACHABLE,
+       netlink_icmp_simple(st,sender,buf,ICMP_TYPE_UNREACHABLE,
                            ICMP_CODE_PROTOCOL_UNREACHABLE,icmp_noinfo);
        BUF_FREE(buf);
        return;
@@ -814,13 +861,13 @@ static void netlink_packet_local(struct netlink *st,
 
 /* If cid==NULL packet is from host, otherwise cid specifies which tunnel 
    it came from. */
-static void netlink_incoming(struct netlink *st, struct netlink_client *client,
+static void netlink_incoming(struct netlink *st, struct netlink_client *sender,
                             struct buffer_if *buf)
 {
     uint32_t source,dest;
     struct iphdr *iph;
     char errmsgbuf[50];
-    const char *sourcedesc=client?client->name:"host";
+    const char *sourcedesc=sender?sender->name:"host";
 
     BUF_ASSERT_USED(buf);
 
@@ -840,16 +887,15 @@ static void netlink_incoming(struct netlink *st, struct netlink_client *client,
     /* Check source. If we don't like the source, there's no point
        generating ICMP because we won't know how to get it to the
        source of the packet. */
-    if (client) {
+    if (sender) {
        /* Check that the packet source is appropriate for the tunnel
           it came down */
-       if (!ipset_contains_addr(client->networks,source)) {
+       if (!ipset_contains_addr(sender->networks,source)) {
            string_t s,d;
            s=ipaddr_to_string(source);
            d=ipaddr_to_string(dest);
            Message(M_WARNING,"%s: packet from tunnel %s with bad "
-                   "source address (s=%s,d=%s)\n",st->name,client->name,s,d);
-           free(s); free(d);
+                   "source address (s=%s,d=%s)\n",st->name,sender->name,s,d);
            BUF_FREE(buf);
            return;
        }
@@ -863,7 +909,6 @@ static void netlink_incoming(struct netlink *st, struct netlink_client *client,
            d=ipaddr_to_string(dest);
            Message(M_WARNING,"%s: outgoing packet with bad source address "
                    "(s=%s,d=%s)\n",st->name,s,d);
-           free(s); free(d);
            BUF_FREE(buf);
            return;
        }
@@ -875,8 +920,8 @@ static void netlink_incoming(struct netlink *st, struct netlink_client *client,
        where it came from.  It's up to external software to check
        address validity and generate ICMP, etc. */
     if (st->ptp) {
-       if (client) {
-           netlink_host_deliver(st,source,dest,buf);
+       if (sender) {
+           netlink_host_deliver(st,sender,source,dest,buf);
        } else {
            netlink_client_deliver(st,st->clients,source,dest,buf);
        }
@@ -887,11 +932,11 @@ static void netlink_incoming(struct netlink *st, struct netlink_client *client,
     /* st->secnet_address needs checking before matching destination
        addresses */
     if (dest==st->secnet_address) {
-       netlink_packet_local(st,client,buf);
+       netlink_packet_local(st,sender,buf);
        BUF_ASSERT_FREE(buf);
        return;
     }
-    netlink_packet_forward(st,client,buf);
+    netlink_packet_forward(st,sender,buf);
     BUF_ASSERT_FREE(buf);
 }
 
@@ -931,7 +976,6 @@ static void netlink_output_subnets(struct netlink *st, uint32_t loglevel,
     for (i=0; i<snets->entries; i++) {
        net=subnet_to_string(snets->list[i]);
        Message(loglevel,"%s ",net);
-       free(net);
     }
 }
 
@@ -946,7 +990,6 @@ static void netlink_dump_routes(struct netlink *st, bool_t requested)
        net=ipaddr_to_string(st->secnet_address);
        Message(c,"%s: point-to-point (remote end is %s); routes: ",
                st->name, net);
-       free(net);
        netlink_output_subnets(st,c,st->clients->subnets);
        Message(c,"\n");
     } else {
@@ -967,11 +1010,9 @@ static void netlink_dump_routes(struct netlink *st, bool_t requested)
        net=ipaddr_to_string(st->secnet_address);
        Message(c,"%s/32 -> netlink \"%s\" (use %d)\n",
                net,st->name,st->localcount);
-       free(net);
        for (i=0; i<st->subnets->entries; i++) {
            net=subnet_to_string(st->subnets->list[i]);
            Message(c,"%s ",net);
-           free(net);
        }
        if (i>0)
            Message(c,"-> host (use %d)\n",st->outcount);
@@ -1028,12 +1069,16 @@ static void netlink_inst_set_mtu(void *sst, int32_t new_mtu)
 }
 
 static void netlink_inst_reg(void *sst, netlink_deliver_fn *deliver, 
-                            void *dst)
+                            void *dst, uint32_t *localmtu_r)
 {
     struct netlink_client *c=sst;
+    struct netlink *st=c->nst;
 
     c->deliver=deliver;
     c->dst=dst;
+
+    if (localmtu_r)
+       *localmtu_r=st->mtu;
 }
 
 static struct flagstr netlink_option_table[]={
@@ -1186,6 +1231,8 @@ netlink_deliver_fn *netlink_init(struct netlink *st,
        st->remote_networks=ipset_complement(empty);
        ipset_free(empty);
     }
+    st->local_address=string_item_to_ipaddr(
+       dict_find_item(dict,"local-address", True, "netlink", loc),"netlink");
 
     sa=dict_find_item(dict,"secnet-address",False,"netlink",loc);
     ptpa=dict_find_item(dict,"ptp-address",False,"netlink",loc);