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);
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)";
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;
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);
/* 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)
{
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);
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);
}
}
/* 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,
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);
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++;
}
/* 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++;
}
/* 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;
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);
}
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 {
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);
/* 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;
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;
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;
}
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;
}
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);
}
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 {
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);
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);