#include <string.h>
#include <stdlib.h>
#include <stdio.h>
+#include <libgen.h>
+#include <sys/stat.h>
#include <limits.h>
#include <arpa/inet.h>
#include <netinet/in.h>
-#ifdef linux
-#include <netinet/ether.h>
-#endif
-
#include "netifd.h"
#include "device.h"
#include "interface.h"
ROUTE_ONLINK,
ROUTE_TYPE,
ROUTE_PROTO,
+ ROUTE_DISABLED,
__ROUTE_MAX
};
[ROUTE_ONLINK] = { .name = "onlink", .type = BLOBMSG_TYPE_BOOL },
[ROUTE_TYPE] = { .name = "type", .type = BLOBMSG_TYPE_STRING },
[ROUTE_PROTO] = { .name = "proto", .type = BLOBMSG_TYPE_STRING },
+ [ROUTE_DISABLED] = { .name = "disabled", .type = BLOBMSG_TYPE_BOOL },
};
const struct uci_blob_param_list route_attr_list = {
if (v6 != ((addr->flags & DEVADDR_FAMILY) == DEVADDR_INET6))
continue;
+ if (((addr->flags & DEVADDR_FAMILY) == DEVADDR_INET4) &&
+ addr->point_to_point && a->in.s_addr == addr->point_to_point)
+ return true;
+
/* Handle offlink addresses correctly */
unsigned int mask = addr->mask;
if ((addr->flags & DEVADDR_FAMILY) == DEVADDR_INET6 &&
ip = &iface->proto_ip;
neighbor = calloc(1,sizeof(*neighbor));
- neighbor->flags = v6 ? DEVADDR_INET6 : DEVADDR_INET4;
-
if (!neighbor)
return;
+ neighbor->flags = v6 ? DEVADDR_INET6 : DEVADDR_INET4;
+
if ((cur = tb[NEIGHBOR_ADDRESS]) != NULL){
if (!inet_pton(af, blobmsg_data(cur), &neighbor->addr))
goto error;
blobmsg_parse(route_attr, __ROUTE_MAX, tb, blobmsg_data(attr), blobmsg_data_len(attr));
+ if ((cur = tb[ROUTE_DISABLED]) != NULL && blobmsg_get_bool(cur))
+ return;
+
if (!iface) {
if ((cur = tb[ROUTE_INTERFACE]) == NULL)
return;
static int
addr_cmp(const void *k1, const void *k2, void *ptr)
{
- return memcmp(k1, k2, sizeof(struct device_addr) -
- offsetof(struct device_addr, flags));
+ const struct device_addr *a1 = k1;
+ const struct device_addr *a2 = k2;
+ const int cmp_offset = offsetof(struct device_addr, flags);
+ const int cmp_size = sizeof(struct device_addr) - cmp_offset;
+
+ if (a1->index != a2->index)
+ return a1->index - a2->index;
+ return memcmp(k1+cmp_offset, k2+cmp_offset, cmp_size);
}
static int
if (!(a_old->flags & DEVADDR_EXTERNAL)) {
interface_handle_subnet_route(iface, a_old, false);
system_del_address(dev, a_old);
+
+ if ((a_old->flags & DEVADDR_OFFLINK) && (a_old->mask < (v6 ? 128 : 32))) {
+ struct device_route route;
+
+ memset(&route, 0, sizeof(route));
+ route.flags = v6 ? DEVADDR_INET6 : DEVADDR_INET4;
+ route.metric = INT32_MAX;
+ route.mask = a_old->mask;
+ route.addr = a_old->addr;
+
+ clear_if_addr(&route.addr, route.mask);
+
+ /* Delete null-route */
+ system_del_route(NULL, &route);
+ }
+
}
}
free(a_old->pclass);
a_new->enabled = true;
if ((a_new->flags & DEVADDR_FAMILY) == DEVADDR_INET6)
- v6 = true;
+ v6 = true;
a_new->policy_table = (v6) ? iface->ip6table : iface->ip4table;
}
if (!keep) {
+ if (!(a_new->flags & DEVADDR_EXTERNAL) &&
+ (a_new->flags & DEVADDR_OFFLINK) &&
+ (a_new->mask < (v6 ? 128 : 32))) {
+ struct device_route route;
+
+ memset(&route, 0, sizeof(route));
+ route.flags = v6 ? DEVADDR_INET6 : DEVADDR_INET4;
+ route.metric = INT32_MAX;
+ route.mask = a_new->mask;
+ route.addr = a_new->addr;
+
+ clear_if_addr(&route.addr, route.mask);
+
+ /*
+ * In case off link is specifed as address property
+ * add null-route to avoid routing loops
+ */
+ system_add_route(NULL, &route);
+ }
+
if (a_new->policy_table)
interface_add_addr_rules(a_new, true);
}
addr.addr.in6 = assignment->addr;
addr.mask = assignment->length;
- addr.flags = DEVADDR_INET6 | DEVADDR_OFFLINK;
+ addr.flags = DEVADDR_INET6;
addr.preferred_until = prefix->preferred_until;
addr.valid_until = prefix->valid_until;
route.addr = addr.addr;
}
+ addr.flags |= DEVADDR_OFFLINK;
if (system_add_address(l3_downlink, &addr))
return;
uint8_t length, time_t valid_until, time_t preferred_until,
struct in6_addr *excl_addr, uint8_t excl_length, const char *pclass)
{
+ union if_addr a = { .in6 = *addr };
+
if (!pclass)
pclass = (iface) ? iface->name : "local";
if (!prefix)
return NULL;
+ clear_if_addr(&a, length);
+
prefix->length = length;
- prefix->addr = *addr;
+ prefix->addr = a.in6;
prefix->preferred_until = preferred_until;
prefix->valid_until = valid_until;
prefix->iface = iface;
if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
continue;
- if (!blobmsg_check_attr(cur, NULL))
+ if (!blobmsg_check_attr(cur, false))
continue;
interface_add_dns_server(ip, blobmsg_data(cur));
if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
continue;
- if (!blobmsg_check_attr(cur, NULL))
+ if (!blobmsg_check_attr(cur, false))
continue;
interface_add_dns_search_domain(ip, blobmsg_data(cur));
}
static void
-__interface_write_dns_entries(FILE *f)
+__interface_write_dns_entries(FILE *f, const char *jail)
{
struct interface *iface;
struct {
if (iface->state != IFS_UP)
continue;
+ if (jail && (!iface->jail || strcmp(jail, iface->jail)))
+ continue;
+
if (vlist_simple_empty(&iface->proto_ip.dns_search) &&
vlist_simple_empty(&iface->proto_ip.dns_servers) &&
vlist_simple_empty(&iface->config_ip.dns_search) &&
}
void
-interface_write_resolv_conf(void)
+interface_write_resolv_conf(const char *jail)
{
- char *path = alloca(strlen(resolv_conf) + 5);
+ size_t plen = (jail ? strlen(jail) + 1 : 0 ) + strlen(resolv_conf) + 1;
+ char *path = alloca(plen);
+ char *dpath = alloca(plen);
+ char *tmppath = alloca(plen + 4);
FILE *f;
uint32_t crcold, crcnew;
- sprintf(path, "%s.tmp", resolv_conf);
- unlink(path);
- f = fopen(path, "w+");
+ if (jail) {
+ sprintf(path, "/tmp/resolv.conf-%s.d/resolv.conf.auto", jail);
+ strcpy(dpath, path);
+ dpath = dirname(dpath);
+ mkdir(dpath, 0755);
+ } else {
+ strcpy(path, resolv_conf);
+ }
+
+ sprintf(tmppath, "%s.tmp", path);
+ unlink(tmppath);
+ f = fopen(tmppath, "w+");
if (!f) {
D(INTERFACE, "Failed to open %s for writing\n", path);
return;
}
- __interface_write_dns_entries(f);
+ __interface_write_dns_entries(f, jail);
fflush(f);
rewind(f);
fclose(f);
crcold = crcnew + 1;
- f = fopen(resolv_conf, "r");
+ f = fopen(path, "r");
if (f) {
crcold = crc32_file(f);
fclose(f);
}
if (crcold == crcnew) {
- unlink(path);
- } else if (rename(path, resolv_conf) < 0) {
- D(INTERFACE, "Failed to replace %s\n", resolv_conf);
- unlink(path);
+ unlink(tmppath);
+ } else if (rename(tmppath, path) < 0) {
+ D(INTERFACE, "Failed to replace %s\n", path);
+ unlink(tmppath);
}
}
if (iface->metric || addr->policy_table)
interface_handle_subnet_route(iface, addr, true);
+ if ((addr->flags & DEVADDR_OFFLINK) && (addr->mask < (v6 ? 128 : 32))) {
+ struct device_route route;
+
+ memset(&route, 0, sizeof(route));
+ route.flags = v6 ? DEVADDR_INET6 : DEVADDR_INET4;
+ route.metric = INT32_MAX;
+ route.mask = addr->mask;
+ route.addr = addr->addr;
+
+ clear_if_addr(&route.addr, route.mask);
+
+ /*
+ * In case off link is specifed as address property
+ * add null-route to avoid routing loops
+ */
+ system_add_route(NULL, &route);
+ }
+
if (addr->policy_table)
interface_add_addr_rules(addr, true);
} else {
interface_handle_subnet_route(iface, addr, false);
system_del_address(dev, addr);
+ if ((addr->flags & DEVADDR_OFFLINK) && (addr->mask < (v6 ? 128 : 32))) {
+ struct device_route route;
+
+ memset(&route, 0, sizeof(route));
+ route.flags = v6 ? DEVADDR_INET6 : DEVADDR_INET4;
+ route.metric = INT32_MAX;
+ route.mask = addr->mask;
+ route.addr = addr->addr;
+
+ clear_if_addr(&route.addr, route.mask);
+
+ /* Delete null-route */
+ system_del_route(NULL, &route);
+ }
+
if (addr->policy_table)
interface_add_addr_rules(addr, false);
}
route->failed = true;
} else
system_del_route(dev, route);
+
route->enabled = _enabled;
}
vlist_flush(&ip->addr);
vlist_flush(&ip->prefix);
vlist_flush(&ip->neighbor);
- interface_write_resolv_conf();
+ interface_write_resolv_conf(ip->iface->jail);
}
void