relay: RD relay between master and slave interfaces
a) support for rewriting announced DNS-server addresses in relay mode
-3. DHCPv6-support with 2 modes of operation
- server: minimalistic server mode
+2. DHCPv6-support with 2 modes of operation
+ server: stateless, stateful and PD-server mode
a) stateless and stateful address assignment
b) prefix delegation support
c) dynamic reconfiguration in case prefixes change
relay: mostly standards-compliant DHCPv6-relay
a) support for rewriting announced DNS-server addresses
+3. DHCPv4-support
+ server: stateless and stateful mode
+
4. Proxy for Neighbor Discovery messages (solicitations and advertisments)
a) support for auto-learning routes to the local routing table
b) support for marking interfaces "external" not proxying NDP for them
* To build DEB or RPM packages use: "make package" afterwards.
-** Server Mode **
+** Configuration **
-0. Server mode is used as a minimalistic alternative for full-blown servers
- like radvd or ISC DHCP if simplicity or a small footprint matter.
- Note: The master interface is unused in this mode. It should be set to '.'.
+odhcpd uses a UCI configuration file in /etc/config/dhcp for configuration
+and may also receive information from ubus
-1. If there are non-local addresses assigned to the slave interface when a
- router solicitation is received, said prefixes are announced automatically
- for stateless autoconfiguration and also offered via stateful DHCPv6.
- If all prefixes are bigger than /64 all but the first /64 of these prefixes
- is offered via DHCPv6-PD to downstream routers.
-2. If DNS servers should be announced (DHCPv6 server-mode) then a local DNS-
- proxy (e.g. dnsmasq) needs to be run on the router itself because 6relayd
- will always announce a local address as DNS-server (if not otherwise
- configured).
+Section of type odhcpd
-3. odhcpd is run with the appropriate configuration.
+Option Type Default Description
+legacy bool 0 Enable DHCPv4 if start but
+ no dhcpv4 option set
+leasefile string DHCP/v6 lease/hostfile
+leasetrigger string Lease trigger script
-** Relay Mode **
+Sections of type dhcp (configure DHCP / DHCPv6 / RA / NDP service)
-0. Relay mode is used when a /64-bit IPv6-Prefix should be distributed over
- several links / isolated layer 2 domains (e.g. if no prefix delegation
- is available). In this mode NDP (namely Router Discovery and Neighbor
- Discovery) messages and DHCPv6-messages are proxied. For DHCPv6 also
- server mode can be used instead of relaying if desired.
+Option Type Default Description
+interface string <name of UCI section> logical OpenWrt interface
+ifname string <resolved from logical> physical network interface
+networkid string same as ifname compat. alias for ifname
+ignore bool 0 ignore this interface
+master bool 0 is a master interface
+ for relaying
+
+ra string disabled Router Advert service
+ [disabled|server|relay|hybrid]
+dhcpv6 string disabled DHCPv6 service
+ [disabled|server|relay|hybrid]
+dhcpv4 string disabled DHCPv4 service
+ [disabled|server]
+ndp string disabled Neighbor Discovery Proxy
+ [disabled|relay|hybrid]
+
+dynamicdhcp bool 1 dynamically create leases
+ for DHCPv4 and DHCPv6
+dns list <local address> DNS servers to announce
+ accepts IPv4 and IPv6
+domain list <local search domain> Search domains to announce
+
+leasetime string 12h DHCPv4 address leasetime
+start integer 100 DHCPv4 pool start
+limit integer 150 DHCPv4 pool size
+
+ula_compat bool 0 Announce ULA in compat mode
+ra_default integer 0 Override default route
+ 0: default, 1: ignore no public address, 2: ignore all
+ra_management integer 1 RA management mode
+ 0: no M-Flag but A-Flag, 1: both M and A, 2: M but not A
+ra_offlink bool 0 Announce prefixes off-link
+ra_preference string medium Route(r) preference
+ [medium|high|low]
+ndproxy_routing bool 0 Learn routes from NDP
+ndproxy_slave bool 0 NDProxy external slave
+ndproxy_static list Static NDProxy prefixes
+
+
+Sections of type lease (static leases)
+Option Type Default Description
+ip string IP-Address to lease
+mac string MAC-address
+duid string DUID in base16
+hostid string IPv6 host identifier
+hostname string Hostname
-1. When starting 6relayd it is required that the master interface - where
- IPv6-service is already provided - is configured and usable.
-
-2. If the upstream router doesn't provide or announce a DNS-service that is
- reachable in an at least site-local scope, a local DNS proxy (e.g. dnsmasq)
- needs to be run and configued on the same router where 6relayd is running.
- (This step can most likely be skipped in most environments.)
-
-3. odhcpd is run with the appropriate configuration.
enum {
IFACE_ATTR_INTERFACE,
IFACE_ATTR_IFNAME,
+ IFACE_ATTR_NETWORKID,
IFACE_ATTR_DYNAMICDHCP,
IFACE_ATTR_IGNORE,
IFACE_ATTR_LEASETIME,
IFACE_ATTR_RA,
IFACE_ATTR_DHCPV4,
IFACE_ATTR_DHCPV6,
- IFACE_ATTR_NDPROXY,
+ IFACE_ATTR_NDP,
IFACE_ATTR_DNS,
IFACE_ATTR_DOMAIN,
IFACE_ATTR_ULA_COMPAT,
static const struct blobmsg_policy iface_attrs[IFACE_ATTR_MAX] = {
[IFACE_ATTR_INTERFACE] = { .name = "interface", .type = BLOBMSG_TYPE_STRING },
[IFACE_ATTR_IFNAME] = { .name = "ifname", .type = BLOBMSG_TYPE_STRING },
+ [IFACE_ATTR_NETWORKID] = { .name = "networkid", .type = BLOBMSG_TYPE_STRING },
[IFACE_ATTR_DYNAMICDHCP] = { .name = "dynamicdhcp", .type = BLOBMSG_TYPE_BOOL },
[IFACE_ATTR_IGNORE] = { .name = "ignore", .type = BLOBMSG_TYPE_BOOL },
[IFACE_ATTR_LEASETIME] = { .name = "leasetime", .type = BLOBMSG_TYPE_STRING },
[IFACE_ATTR_RA] = { .name = "ra", .type = BLOBMSG_TYPE_STRING },
[IFACE_ATTR_DHCPV4] = { .name = "dhcpv4", .type = BLOBMSG_TYPE_STRING },
[IFACE_ATTR_DHCPV6] = { .name = "dhcpv6", .type = BLOBMSG_TYPE_STRING },
- [IFACE_ATTR_NDPROXY] = { .name = "ndproxy", .type = BLOBMSG_TYPE_BOOL },
+ [IFACE_ATTR_NDP] = { .name = "ndp", .type = BLOBMSG_TYPE_STRING },
[IFACE_ATTR_DNS] = { .name = "dns", .type = BLOBMSG_TYPE_ARRAY },
[IFACE_ATTR_DOMAIN] = { .name = "domain", .type = BLOBMSG_TYPE_ARRAY },
[IFACE_ATTR_ULA_COMPAT] = { .name = "ula_compat", .type = BLOBMSG_TYPE_BOOL },
#endif
if ((c = tb[IFACE_ATTR_IFNAME]))
ifname = blobmsg_get_string(c);
+ else if ((c = tb[IFACE_ATTR_NETWORKID]))
+ ifname = blobmsg_get_string(c);
strncpy(iface->ifname, ifname, sizeof(iface->ifname) - 1);
iface->inuse = true;
if ((iface->dhcpv6 = parse_mode(blobmsg_get_string(c))) < 0)
goto err;
- if ((c = tb[IFACE_ATTR_NDPROXY]))
- iface->ndp = blobmsg_get_bool(c) ? RELAYD_RELAY : RELAYD_DISABLED;
+ if ((c = tb[IFACE_ATTR_NDP]))
+ if ((iface->ndp = parse_mode(blobmsg_get_string(c))) < 0)
+ goto err;
if ((c = tb[IFACE_ATTR_DNS])) {
struct blob_attr *cur;
int rem;
+ iface->always_rewrite_dns = true;
blobmsg_for_each_attr(cur, c, rem) {
if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING || !blobmsg_check_attr(cur, NULL))
continue;
continue;
enum odhcpd_mode hybrid_mode = RELAYD_DISABLED;
+#ifdef WITH_UBUS
+ if (ubus_has_prefix(i->name, i->ifname))
+ hybrid_mode = RELAYD_RELAY;
+#endif
+
if (i->dhcpv6 == RELAYD_HYBRID)
i->dhcpv6 = hybrid_mode;
end = addr.s_addr & mask.s_addr;
if (ntohl(mask.s_addr) <= 0xffffff00) {
- iface->dhcpv4_start.s_addr = start | htonl(20);
- iface->dhcpv4_end.s_addr = end | htonl(199);
+ iface->dhcpv4_start.s_addr = start | htonl(100);
+ iface->dhcpv4_end.s_addr = end | htonl(250);
} else {
iface->dhcpv4_start.s_addr = start | htonl(10);
iface->dhcpv4_end.s_addr = end | htonl(59);
if (iface->dhcpv4_leasetime < 60)
- iface->dhcpv4_leasetime = 1800;
+ iface->dhcpv4_leasetime = 43200;
iface->dhcpv4_event.uloop.fd = sock;
iface->dhcpv4_event.handle_dgram = handle_dhcpv4;
const char* ubus_get_ifname(const char *name);
void ubus_apply_network(void);
+bool ubus_has_prefix(const char *name, const char *ifname);
// Exported module initializers
IFACE_ATTR_IFNAME,
IFACE_ATTR_UP,
IFACE_ATTR_DATA,
+ IFACE_ATTR_PREFIX,
IFACE_ATTR_MAX,
};
[IFACE_ATTR_IFNAME] = { .name = "ifname", .type = BLOBMSG_TYPE_STRING },
[IFACE_ATTR_UP] = { .name = "up", .type = BLOBMSG_TYPE_BOOL },
[IFACE_ATTR_DATA] = { .name = "data", .type = BLOBMSG_TYPE_TABLE },
+ [IFACE_ATTR_PREFIX] = { .name = "ipv6-prefix", .type = BLOBMSG_TYPE_ARRAY },
};
static void handle_dump(_unused struct ubus_request *req, _unused int type, struct blob_attr *msg)
}
+bool ubus_has_prefix(const char *name, const char *ifname)
+{
+ struct blob_attr *c, *cur;
+ int rem;
+
+ if (!dump)
+ return NULL;
+
+ blobmsg_for_each_attr(c, dump, rem) {
+ struct blob_attr *tb[IFACE_ATTR_MAX];
+ blobmsg_parse(iface_attrs, IFACE_ATTR_MAX, tb, blob_data(c), blob_len(c));
+
+ if (!tb[IFACE_ATTR_INTERFACE] || !tb[IFACE_ATTR_IFNAME])
+ continue;
+
+ if (!strcmp(name, blobmsg_get_string(tb[IFACE_ATTR_INTERFACE])) ||
+ !strcmp(ifname, blobmsg_get_string(tb[IFACE_ATTR_IFNAME])))
+ continue;
+
+ if ((cur = tb[IFACE_ATTR_PREFIX])) {
+ if (blobmsg_type(cur) != BLOBMSG_TYPE_ARRAY || !blobmsg_check_attr(cur, NULL))
+ continue;
+
+ struct blob_attr *d;
+ int drem;
+ blobmsg_for_each_attr(d, cur, drem) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+
int init_ubus(void)
{
if (!(ubus = ubus_connect(NULL))) {