#include "secnet.h"
#include "util.h"
+#include "ipaddr.h"
#include "netlink.h"
+#include "process.h"
/* Generic IP checksum routine */
static inline uint16_t ip_csum(uint8_t *iph,uint32_t count)
static bool_t netlink_icmp_may_reply(struct buffer_if *buf)
{
struct iphdr *iph;
+ struct icmphdr *icmph;
uint32_t source;
iph=(struct iphdr *)buf->start;
- if (iph->protocol==1) return False; /* Overly-broad; we may reply to
- eg. icmp echo-request */
+ icmph=(struct icmphdr *)buf->start;
+ if (iph->protocol==1) {
+ switch(icmph->type) {
+ case 3: /* Destination unreachable */
+ case 11: /* Time Exceeded */
+ case 12: /* Parameter Problem */
+ return False;
+ }
+ }
/* How do we spot broadcast destination addresses? */
if (ntohs(iph->frag_off)&0x1fff) return False; /* Non-initial fragment */
source=ntohl(iph->saddr);
/* Check source */
if (client) {
- /* Check that the packet source is in 'nets' and its destination is
- in st->networks */
+ /* Check that the packet source is appropriate for the tunnel
+ it came down */
if (!subnet_matches_list(client->networks,source)) {
string_t s,d;
s=ipaddr_to_string(source);
return;
}
} else {
+ /* Check that the packet originates in our configured local
+ network, and hasn't been forwarded from elsewhere or
+ generated with the wrong source address */
if (!subnet_matches_list(&st->networks,source)) {
string_t s,d;
s=ipaddr_to_string(source);
return;
}
}
+
+ /* If this is a point-to-point device we don't examine the packet at
+ all; we blindly send it down our one-and-only registered tunnel,
+ or to the host, depending on where it came from. */
+ if (st->ptp) {
+ if (client) {
+ st->deliver_to_host(st->dst,NULL,buf);
+ } else {
+ st->clients->deliver(st->clients->dst,NULL,buf);
+ }
+ BUF_ASSERT_FREE(buf);
+ return;
+ }
+
/* (st->secnet_address needs checking before matching destination
addresses) */
if (dest==st->secnet_address) {
}
static void netlink_set_softlinks(struct netlink *st, struct netlink_client *c,
- bool_t up)
+ bool_t up, uint32_t quality)
{
uint32_t i;
if (!st->routes) return; /* Table has not yet been created */
for (i=0; i<st->n_routes; i++) {
- if (!st->routes[i].hard && st->routes[i].c==c) {
- st->routes[i].up=up;
- st->set_route(st->dst,&st->routes[i]);
+ if (st->routes[i].c==c) {
+ st->routes[i].quality=quality;
+ if (!st->routes[i].hard) {
+ st->routes[i].up=up;
+ st->set_route(st->dst,&st->routes[i]);
+ }
}
}
}
c->link_quality=quality;
if (c->link_quality==LINK_QUALITY_DOWN) {
- netlink_set_softlinks(st,c,False);
+ netlink_set_softlinks(st,c,False,c->link_quality);
} else {
- netlink_set_softlinks(st,c,True);
+ netlink_set_softlinks(st,c,True,c->link_quality);
}
}
Message(M_ERROR,"%s: site %s specifies networks that "
"intersect with the explicitly excluded remote networks\n",
st->name,client_name);
- return False;
+ return NULL;
+ }
+
+ if (st->clients && st->ptp) {
+ fatal("%s: only one site may use a point-to-point netlink device\n",
+ st->name);
+ return NULL;
}
c=safe_malloc(sizeof(*c),"netlink_regnets");
c->networks=nets;
c->deliver=deliver;
c->dst=dst;
- c->name=client_name; /* XXX copy it? */
+ c->name=client_name;
c->options=options;
c->link_quality=LINK_QUALITY_DOWN;
c->next=st->clients;
return c;
}
-static void netlink_dump_routes(struct netlink *st)
+static void netlink_dump_routes(struct netlink *st, bool_t requested)
{
int i;
string_t net;
+ uint32_t c=M_INFO;
- Message(M_INFO,"%s: routing table:\n",st->name);
+ if (requested) c=M_WARNING;
+ Message(c,"%s: routing table:\n",st->name);
for (i=0; i<st->n_routes; i++) {
net=subnet_to_string(&st->routes[i].net);
- Message(M_INFO,"%s -> tunnel %s (%s,%s route,%s)\n",net,
+ Message(c,"%s -> tunnel %s (%s,%s route,%s,quality %d)\n",net,
st->routes[i].c->name,
st->routes[i].hard?"hard":"soft",
st->routes[i].allow_route?"free":"restricted",
- st->routes[i].up?"up":"down");
+ st->routes[i].up?"up":"down",
+ st->routes[i].quality);
free(net);
}
- Message(M_INFO,"%s/32 -> netlink \"%s\"\n",
+ Message(c,"%s/32 -> netlink \"%s\"\n",
ipaddr_to_string(st->secnet_address),st->name);
for (i=0; i<st->networks.entries; i++) {
net=subnet_to_string(&st->networks.list[i]);
- Message(M_INFO,"%s -> host\n",net);
+ Message(c,"%s -> host\n",net);
free(net);
}
}
struct netlink_client *c;
uint32_t i,j;
+ if (!st->clients && st->ptp) {
+ /* Point-to-point netlink devices must have precisely one
+ client. If none has registered by now, complain. */
+ fatal("%s: point-to-point netlink devices must have precisely "
+ "one client. This one doesn't have any.\n",st->name);
+ }
+
/* All the networks serviced by the various tunnels should now
* have been registered. We build a routing table by sorting the
* routes into most-specific-first order. */
st->routes[i].hard=c->options&NETLINK_OPTION_SOFTROUTE?False:True;
st->routes[i].allow_route=c->options&NETLINK_OPTION_ALLOW_ROUTE?
True:False;
+ st->routes[i].quality=c->link_quality;
i++;
}
}
qsort(st->routes,st->n_routes,sizeof(*st->routes),
netlink_compare_route_specificity);
- netlink_dump_routes(st);
+ netlink_dump_routes(st,False);
+}
+
+static void netlink_signal_handler(void *sst, int signum)
+{
+ struct netlink *st=sst;
+ Message(M_INFO,"%s: route dump requested by SIGUSR1\n",st->name);
+ netlink_dump_routes(st,True);
}
netlink_deliver_fn *netlink_init(struct netlink *st,
netlink_route_fn *set_route,
netlink_deliver_fn *to_host)
{
+ item_t *sa, *ptpa;
+
st->dst=dst;
st->cl.description=description;
st->cl.type=CL_NETLINK;
&st->networks);
dict_read_subnet_list(dict, "exclude-remote-networks", False, "netlink",
loc, &st->exclude_remote_networks);
- /* local-address and secnet-address do not have to be in local-networks;
- however, they should be advertised in the 'sites' file for the
+ /* secnet-address does not have to be in local-networks;
+ however, it should be advertised in the 'sites' file for the
local site. */
- st->local_address=string_to_ipaddr(
- dict_find_item(dict,"local-address", True, "netlink", loc),"netlink");
- st->secnet_address=string_to_ipaddr(
- dict_find_item(dict,"secnet-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);
+ if (sa && ptpa) {
+ cfgfatal(loc,st->name,"you may not specify secnet-address and "
+ "ptp-address in the same netlink device\n");
+ }
+ if (!(sa || ptpa)) {
+ cfgfatal(loc,st->name,"you must specify secnet-address or "
+ "ptp-address for this netlink device\n");
+ }
+ if (sa) {
+ st->secnet_address=string_to_ipaddr(sa,"netlink");
+ st->ptp=False;
+ } else {
+ st->secnet_address=string_to_ipaddr(ptpa,"netlink");
+ st->ptp=True;
+ }
st->mtu=dict_read_number(dict, "mtu", False, "netlink", loc, DEFAULT_MTU);
buffer_new(&st->icmp,ICMP_BUFSIZE);
st->n_routes=0;
st->routes=NULL;
add_hook(PHASE_SETUP,netlink_phase_hook,st);
+ request_signal_notification(SIGUSR1, netlink_signal_handler, st);
return netlink_incoming;
}