X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?p=secnet.git;a=blobdiff_plain;f=netlink.c;h=e197e80f8c69508f5f617d9af86b1bfa07957098;hp=41968e4dc6ffc1c56f44cdb21d6c8b89623b1aea;hb=4ac7fd3ff73c98100d8a8546668d0212e4af3ffa;hpb=f2b711bd3994c413d98b6882ce31b84ec2782789 diff --git a/netlink.c b/netlink.c index 41968e4..e197e80 100644 --- 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,10 +648,11 @@ 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++; } @@ -615,7 +665,7 @@ static void netlink_packet_deliver(struct netlink *st, { if (buf->size < (int)sizeof(struct iphdr)) { Message(M_ERR,"%s: trying to deliver a too-short packet" - " from %s!\n",st->name, sender?sender->name:"(local)"); + " from %s!\n",st->name, sender_name(sender)); BUF_FREE(buf); return; } @@ -685,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; @@ -693,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); } @@ -709,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 { @@ -721,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); @@ -744,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; @@ -802,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; @@ -848,7 +896,6 @@ static void netlink_incoming(struct netlink *st, struct netlink_client *sender, d=ipaddr_to_string(dest); Message(M_WARNING,"%s: packet from tunnel %s with bad " "source address (s=%s,d=%s)\n",st->name,sender->name,s,d); - free(s); free(d); BUF_FREE(buf); return; } @@ -862,7 +909,6 @@ static void netlink_incoming(struct netlink *st, struct netlink_client *sender, 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,7 +921,7 @@ static void netlink_incoming(struct netlink *st, struct netlink_client *sender, address validity and generate ICMP, etc. */ if (st->ptp) { if (sender) { - netlink_host_deliver(st,source,dest,buf); + netlink_host_deliver(st,sender,source,dest,buf); } else { netlink_client_deliver(st,st->clients,source,dest,buf); } @@ -930,7 +976,6 @@ static void netlink_output_subnets(struct netlink *st, uint32_t loglevel, for (i=0; ientries; i++) { net=subnet_to_string(snets->list[i]); Message(loglevel,"%s ",net); - free(net); } } @@ -945,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 { @@ -966,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; isubnets->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); @@ -1189,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);