From: Roy Marples Date: Tue, 28 Jul 2015 13:58:31 +0000 (+0000) Subject: Move route state into it's own flag. X-Git-Tag: v6.9.2~22 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ca6cdf5847cda720b65793ea6827b1b373c62382;p=thirdparty%2Fdhcpcd.git Move route state into it's own flag. Instead of setting the interface MTU directly, set the MTU for each of the routes derived from the DHCP message in the same way we do for IPv6 RA. --- diff --git a/dhcp.c b/dhcp.c index 713b3be4..e7fc9e46 100644 --- a/dhcp.c +++ b/dhcp.c @@ -275,6 +275,23 @@ get_option_uint32(struct dhcpcd_ctx *ctx, return 0; } +static int +get_option_uint16(struct dhcpcd_ctx *ctx, + uint16_t *i, const struct dhcp_message *dhcp, uint8_t option) +{ + const uint8_t *p; + size_t len; + uint16_t d; + + p = get_option(ctx, dhcp, option, &len); + if (!p || len < (ssize_t)sizeof(d)) + return -1; + memcpy(&d, p, sizeof(d)); + if (i) + *i = ntohs(d); + return 0; +} + static int get_option_uint8(struct dhcpcd_ctx *ctx, uint8_t *i, const struct dhcp_message *dhcp, uint8_t option) @@ -583,7 +600,7 @@ route_netmask(uint32_t ip_in) /* We need to obey routing options. * If we have a CSR then we only use that. * Otherwise we add static routes and then routers. */ -struct rt_head * +static struct rt_head * get_option_routes(struct interface *ifp, const struct dhcp_message *dhcp) { struct if_options *ifo = ifp->options; @@ -689,6 +706,40 @@ get_option_routes(struct interface *ifp, const struct dhcp_message *dhcp) return routes; } +uint16_t +dhcp_get_mtu(const struct interface *ifp) +{ + const struct dhcp_message *dhcp; + uint16_t mtu; + + if ((dhcp = D_CSTATE(ifp)->new) == NULL || + has_option_mask(ifp->options->nomask, DHO_MTU) || + get_option_uint16(ifp->ctx, &mtu, dhcp, DHO_MTU) == -1) + return 0; + return mtu; +} + +/* Grab our routers from the DHCP message and apply any MTU value + * the message contains */ +struct rt_head * +dhcp_get_routes(struct interface *ifp) +{ + struct rt_head *routes; + uint16_t mtu; + const struct dhcp_message *dhcp; + + dhcp = D_CSTATE(ifp)->new; + routes = get_option_routes(ifp, dhcp); + if ((mtu = dhcp_get_mtu(ifp)) != 0) { + struct rt *rt; + + TAILQ_FOREACH(rt, routes, next) { + rt->mtu = mtu; + } + } + return routes; +} + #define PUTADDR(_type, _val) \ { \ *p++ = _type; \ diff --git a/dhcp.h b/dhcp.h index daa4f560..1495aafb 100644 --- a/dhcp.h +++ b/dhcp.h @@ -245,8 +245,8 @@ int get_option_addr(struct dhcpcd_ctx *,struct in_addr *, const struct dhcp_message *, uint8_t); #define IS_BOOTP(i, m) ((m) != NULL && \ get_option_uint8((i)->ctx, NULL, (m), DHO_MESSAGETYPE) == -1) -struct rt_head *get_option_routes(struct interface *, - const struct dhcp_message *); +uint16_t dhcp_get_mtu(const struct interface *); +struct rt_head *dhcp_get_routes(struct interface *); ssize_t dhcp_env(char **, const char *, const struct dhcp_message *, const struct interface *); diff --git a/dhcpcd-hooks/10-mtu b/dhcpcd-hooks/10-mtu deleted file mode 100644 index f563624c..00000000 --- a/dhcpcd-hooks/10-mtu +++ /dev/null @@ -1,43 +0,0 @@ -# Configure the MTU for the interface - -mtu_dir="$state_dir/mtu" - -set_mtu() -{ - local mtu=$1 - - if [ -w /sys/class/net/$interface/mtu ]; then - echo "$mtu" >/sys/class/net/$interface/mtu - else - ifconfig "$interface" mtu "$mtu" - fi -} - -# Only set interface MTU on either a DHCP option recieved or a static -# configuration for IPv4 which is wrapped in the DHCP setup. -# IPv6 MTU is done via routing. -if [ "$ifsuffix" = ".dhcp" ]; then - if [ "$reason" = PREINIT -a -e "$mtu_dir/$interface" ]; then - rm "$mtu_dir/$interface" - elif [ -n "$new_interface_mtu" ] && $if_up; then - # The smalled MTU dhcpcd can work with is 576 - if [ "$new_interface_mtu" -ge 576 ]; then - if set_mtu "$new_interface_mtu"; then - syslog info "MTU set to $new_interface_mtu" - # Save the MTU so we can restore it later - if [ ! -e "$mtu_dir/$interface" ]; then - mkdir -p "$mtu_dir" - echo "$ifmtu" > "$mtu_dir/$interface" - fi - fi - fi - elif [ -e "$mtu_dir/$interface" ]; then - if $if_up || $if_down; then - # No MTU in this state, so restore the prior MTU - mtu=$(cat "$mtu_dir/$interface") - syslog info "MTU restored to $mtu" - set_mtu "$mtu" - rm "$mtu_dir/$interface" - fi - fi -fi diff --git a/dhcpcd-hooks/Makefile b/dhcpcd-hooks/Makefile index 63285090..24c9726d 100644 --- a/dhcpcd-hooks/Makefile +++ b/dhcpcd-hooks/Makefile @@ -4,7 +4,7 @@ include ${TOP}/iconfig.mk SCRIPTSDIR= ${LIBEXECDIR}/dhcpcd-hooks SCRIPTS= 01-test 02-dump -SCRIPTS+= 10-mtu 10-wpa_supplicant 15-timezone 20-resolv.conf +SCRIPTS+= 10-wpa_supplicant 15-timezone 20-resolv.conf SCRIPTS+= 29-lookup-hostname 30-hostname SCRIPTS+= ${HOOKSCRIPTS} diff --git a/dhcpcd.8.in b/dhcpcd.8.in index 9f29a505..4d46d52e 100644 --- a/dhcpcd.8.in +++ b/dhcpcd.8.in @@ -22,7 +22,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd July 3, 2015 +.Dd July 28, 2015 .Dt DHCPCD 8 .Os .Sh NAME @@ -551,8 +551,8 @@ Matches full name, or prefixed with 2 numbers optionally ending with .Pp So to stop .Nm -from touching your DNS or MTU settings you would do:- -.D1 dhcpcd -C resolv.conf -C mtu eth0 +from touching your DNS settings you would do:- +.D1 dhcpcd -C resolv.conf eth0 .It Fl G , Fl Fl nogateway Don't set any default routes. .It Fl H , Fl Fl xidhwaddr diff --git a/dhcpcd.conf b/dhcpcd.conf index e5a19cd8..31085994 100644 --- a/dhcpcd.conf +++ b/dhcpcd.conf @@ -28,9 +28,8 @@ option domain_name_servers, domain_name, domain_search, host_name option classless_static_routes # Most distributions have NTP support. option ntp_servers -# Respect the network MTU. -# Some interface drivers reset when changing the MTU so disabled by default. -#option interface_mtu +# Respect the network MTU. This is applied to DHCP routes. +option interface_mtu # A ServerID is required by RFC2131. require dhcp_server_identifier diff --git a/dhcpcd.conf.5.in b/dhcpcd.conf.5.in index d7025573..1f9dbf52 100644 --- a/dhcpcd.conf.5.in +++ b/dhcpcd.conf.5.in @@ -22,7 +22,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd July 22, 2015 +.Dd July 28, 2015 .Dt DHCPCD.CONF 5 .Os .Sh NAME @@ -430,8 +430,8 @@ Matches full name, or prefixed with 2 numbers optionally ending with .Pp So to stop .Nm dhcpcd -from touching your DNS or MTU settings you would do:- -.D1 nohook resolv.conf, mtu +from touching your DNS settings or starting wpa_supplicant you would do:- +.D1 nohook resolv.conf, wpa_supplicant .It Ic noipv4 Don't attempt to configure an IPv4 address. .It Ic noipv4ll diff --git a/if-bsd.c b/if-bsd.c index 2f0c1fff..23864e76 100644 --- a/if-bsd.c +++ b/if-bsd.c @@ -500,6 +500,7 @@ if_copyrt(struct dhcpcd_ctx *ctx, struct rt *rt, struct rt_msghdr *rtm) get_addrs(rtm->rtm_addrs, cp, rti_info); memset(rt, 0, sizeof(*rt)); + rt->flags = (unsigned int)rtm->rtm_flags; COPYOUT(rt->dest, rti_info[RTAX_DST]); if (rtm->rtm_addrs & RTA_NETMASK) COPYOUT(rt->net, rti_info[RTAX_NETMASK]); @@ -508,6 +509,9 @@ if_copyrt(struct dhcpcd_ctx *ctx, struct rt *rt, struct rt_msghdr *rtm) COPYOUT(rt->gate, rti_info[RTAX_GATEWAY]); COPYOUT(rt->src, rti_info[RTAX_IFA]); + if (rtm->rtm_inits & RTV_MTU) + rt->mtu = (unsigned int)rtm->rtm_rmx.rmx_mtu; + if (rtm->rtm_index) rt->iface = if_findindex(ctx->ifaces, rtm->rtm_index); else if (rtm->rtm_addrs & RTA_IFP) { @@ -516,6 +520,7 @@ if_copyrt(struct dhcpcd_ctx *ctx, struct rt *rt, struct rt_msghdr *rtm) sdl = (struct sockaddr_dl *)(void *)rti_info[RTAX_IFP]; rt->iface = if_findsdl(ctx, sdl); } + /* If we don't have an interface and it's a host route, it maybe * to a local ip via the loopback interface. */ if (rt->iface == NULL && @@ -658,6 +663,11 @@ if_route(unsigned char cmd, const struct rt *rt) if (rtm.hdr.rtm_addrs & RTA_IFA) ADDADDR(&state->addr); + + if (rt->mtu) { + rtm.hdr.rtm_inits |= RTV_MTU; + rtm.hdr.rtm_rmx.rmx_mtu = rt->mtu; + } } #undef ADDADDR @@ -897,6 +907,9 @@ if_copyrt6(struct dhcpcd_ctx *ctx, struct rt6 *rt, struct rt_msghdr *rtm) ipv6_mask(&rt->net, 128); COPYOUT6(rt->gate, rti_info[RTAX_GATEWAY]); + if (rtm->rtm_inits & RTV_MTU) + rt->mtu = (unsigned int)rtm->rtm_rmx.rmx_mtu; + if (rtm->rtm_index) rt->iface = if_findindex(ctx->ifaces, rtm->rtm_index); else if (rtm->rtm_addrs & RTA_IFP) { diff --git a/if-linux.c b/if-linux.c index 8b08822a..727c2c94 100644 --- a/if-linux.c +++ b/if-linux.c @@ -922,6 +922,27 @@ add_attr_32(struct nlmsghdr *n, unsigned short maxlen, unsigned short type, return 0; } +static int +rta_add_attr_32(struct rtattr *rta, unsigned short maxlen, + unsigned short type, uint32_t data) +{ + unsigned short len = RTA_LENGTH(sizeof(data)); + struct rtattr *subrta; + + if (RTA_ALIGN(rta->rta_len) + len > maxlen) { + errno = ENOBUFS; + return -1; + } + + subrta = (struct rtattr*)(void *) + (((char*)rta) + RTA_ALIGN(rta->rta_len)); + subrta->rta_type = type; + subrta->rta_len = len; + memcpy(RTA_DATA(subrta), &data, sizeof(data)); + rta->rta_len = (unsigned short)(NLMSG_ALIGN(rta->rta_len) + len); + return 0; +} + #ifdef HAVE_NL80211_H static struct nlattr * nla_next(struct nlattr *nla, size_t *rem) @@ -1374,6 +1395,17 @@ if_route(unsigned char cmd, const struct rt *rt) if (rt->metric) add_attr_32(&nlm.hdr, sizeof(nlm), RTA_PRIORITY, rt->metric); + if (cmd != RTM_DELETE && rt->mtu) { + char metricsbuf[32]; + struct rtattr *metrics = (void *)metricsbuf; + + metrics->rta_type = RTA_METRICS; + metrics->rta_len = RTA_LENGTH(0); + rta_add_attr_32(metrics, sizeof(metricsbuf), RTAX_MTU, rt->mtu); + add_attr_l(&nlm.hdr, sizeof(nlm), RTA_METRICS, + RTA_DATA(metrics), (unsigned short)RTA_PAYLOAD(metrics)); + } + if (send_netlink(rt->iface->ctx, NULL, NETLINK_ROUTE, &nlm.hdr, NULL) == -1) retval = -1; @@ -1488,27 +1520,6 @@ if_address6(const struct ipv6_addr *ap, int action) return retval; } -static int -rta_add_attr_32(struct rtattr *rta, unsigned short maxlen, - unsigned short type, uint32_t data) -{ - unsigned short len = RTA_LENGTH(sizeof(data)); - struct rtattr *subrta; - - if (RTA_ALIGN(rta->rta_len) + len > maxlen) { - errno = ENOBUFS; - return -1; - } - - subrta = (struct rtattr*)(void *) - (((char*)rta) + RTA_ALIGN(rta->rta_len)); - subrta->rta_type = type; - subrta->rta_len = len; - memcpy(RTA_DATA(subrta), &data, sizeof(data)); - rta->rta_len = (unsigned short)(NLMSG_ALIGN(rta->rta_len) + len); - return 0; -} - int if_route6(unsigned char cmd, const struct rt6 *rt) { @@ -1565,7 +1576,7 @@ if_route6(unsigned char cmd, const struct rt6 *rt) add_attr_32(&nlm.hdr, sizeof(nlm), RTA_PRIORITY, rt->metric); } - if (cmd == RTM_ADD && rt->mtu) { + if (cmd != RTM_DELETE && rt->mtu) { char metricsbuf[32]; struct rtattr *metrics = (void *)metricsbuf; diff --git a/ipv4.c b/ipv4.c index 462585cb..022c0e78 100644 --- a/ipv4.c +++ b/ipv4.c @@ -385,6 +385,7 @@ ipv4_handlert(struct dhcpcd_ctx *ctx, int cmd, struct rt *rt) static int nc_route(struct rt *ort, struct rt *nrt) { + int change; /* Don't set default routes if not asked to */ if (nrt->dest.s_addr == 0 && @@ -394,6 +395,7 @@ nc_route(struct rt *ort, struct rt *nrt) desc_route(ort == NULL ? "adding" : "changing", nrt); + change = 0; if (ort == NULL) { ort = ipv4_findrt(nrt->iface->ctx, nrt, 0); if (ort && @@ -403,8 +405,12 @@ nc_route(struct rt *ort, struct rt *nrt) ort->metric == nrt->metric && #endif ort->gate.s_addr == nrt->gate.s_addr))) - return 0; - } else if (ort->flags & STATE_FAKE && !(nrt->flags & STATE_FAKE) && + { + if (ort->mtu == nrt->mtu) + return 0; + change = 1; + } + } else if (ort->state & STATE_FAKE && !(nrt->state & STATE_FAKE) && ort->iface == nrt->iface && #ifdef HAVE_ROUTE_METRIC ort->metric == nrt->metric && @@ -412,7 +418,27 @@ nc_route(struct rt *ort, struct rt *nrt) ort->dest.s_addr == nrt->dest.s_addr && ort->net.s_addr == nrt->net.s_addr && ort->gate.s_addr == nrt->gate.s_addr) - return 0; + { + if (ort->mtu == nrt->mtu) + return 0; + change = 1; + } + +#ifdef RTF_CLONING + /* BSD can set routes to be cloning routes. + * Cloned routes inherit the parent flags. + * As such, we need to delete and re-add the route to flush children + * to correct the flags. */ + if (change && ort != NULL && ort->flags & RTF_CLONING) + change = 0; +#endif + + if (change) { + if (if_route(RTM_CHANGE, nrt) == 0) + return 0; + if (errno != ESRCH) + logger(nrt->iface->ctx, LOG_ERR, "if_route (CHG): %m"); + } #ifdef HAVE_ROUTE_METRIC /* With route metrics, we can safely add the new route before @@ -474,7 +500,7 @@ add_subnet_route(struct rt_head *rt, const struct interface *ifp) return rt; #endif - if ((r = malloc(sizeof(*r))) == NULL) { + if ((r = calloc(1, sizeof(*r))) == NULL) { logger(ifp->ctx, LOG_ERR, "%s: %m", __func__); ipv4_freeroutes(rt); return NULL; @@ -482,6 +508,7 @@ add_subnet_route(struct rt_head *rt, const struct interface *ifp) r->dest.s_addr = s->addr.s_addr & s->net.s_addr; r->net.s_addr = s->net.s_addr; r->gate.s_addr = INADDR_ANY; + r->mtu = dhcp_get_mtu(ifp); r->src = s->addr; TAILQ_INSERT_HEAD(rt, r, next); @@ -502,8 +529,7 @@ add_loopback_route(struct rt_head *rt, const struct interface *ifp) if (s->addr.s_addr == INADDR_ANY) return rt; - r = malloc(sizeof(*r)); - if (r == NULL) { + if ((r = calloc(1, sizeof(*r))) == NULL) { logger(ifp->ctx, LOG_ERR, "%s: %m", __func__); ipv4_freeroutes(rt); return NULL; @@ -511,6 +537,7 @@ add_loopback_route(struct rt_head *rt, const struct interface *ifp) r->dest = s->addr; r->net.s_addr = INADDR_BROADCAST; r->gate.s_addr = htonl(INADDR_LOOPBACK); + r->mtu = dhcp_get_mtu(ifp); r->src = s->addr; TAILQ_INSERT_HEAD(rt, r, next); return rt; @@ -531,8 +558,7 @@ get_routes(struct interface *ifp) TAILQ_FOREACH(rt, ifp->options->routes, next) { if (rt->gate.s_addr == 0) break; - r = malloc(sizeof(*r)); - if (r == NULL) { + if ((r = calloc(1, sizeof(*r))) == NULL) { logger(ifp->ctx, LOG_ERR, "%s: %m", __func__); ipv4_freeroutes(nrt); return NULL; @@ -541,7 +567,7 @@ get_routes(struct interface *ifp) TAILQ_INSERT_TAIL(nrt, r, next); } } else - nrt = get_option_routes(ifp, D_STATE(ifp)->new); + nrt = dhcp_get_routes(ifp); /* Copy our address as the source address */ if (nrt) { @@ -566,8 +592,7 @@ add_destination_route(struct rt_head *rt, const struct interface *ifp) (state = D_CSTATE(ifp)) == NULL) return rt; - r = malloc(sizeof(*r)); - if (r == NULL) { + if ((r = calloc(1, sizeof(*r))) == NULL) { logger(ifp->ctx, LOG_ERR, "%s: %m", __func__); ipv4_freeroutes(rt); return NULL; @@ -575,6 +600,8 @@ add_destination_route(struct rt_head *rt, const struct interface *ifp) r->dest.s_addr = INADDR_ANY; r->net.s_addr = INADDR_ANY; r->gate.s_addr = state->dst.s_addr; + r->mtu = dhcp_get_mtu(ifp); + r->src = state->addr; TAILQ_INSERT_HEAD(rt, r, next); return rt; } @@ -639,8 +666,7 @@ add_router_host_route(struct rt_head *rt, const struct interface *ifp) "%s: router %s requires a host route", ifp->name, inet_ntoa(rtp->gate)); } - rtn = malloc(sizeof(*rtn)); - if (rtn == NULL) { + if ((rtn = calloc(1, sizeof(*rtn))) == NULL) { logger(ifp->ctx, LOG_ERR, "%s: %m", __func__); ipv4_freeroutes(rt); return NULL; @@ -648,6 +674,8 @@ add_router_host_route(struct rt_head *rt, const struct interface *ifp) rtn->dest.s_addr = rtp->gate.s_addr; rtn->net.s_addr = htonl(INADDR_BROADCAST); rtn->gate.s_addr = htonl(INADDR_ANY); + rtn->mtu = dhcp_get_mtu(ifp); + rtn->src = state->addr; TAILQ_INSERT_BEFORE(rtp, rtn, next); } return rt; @@ -660,10 +688,10 @@ ipv4_doroute(struct rt *rt, struct rt_head *nrs) struct rt *or; state = D_CSTATE(rt->iface); + rt->state = state->added & STATE_FAKE; #ifdef HAVE_ROUTE_METRIC rt->metric = rt->iface->metric; #endif - rt->flags = state->added & STATE_FAKE; /* Is this route already in our table? */ if ((find_route(nrs, rt, NULL)) != NULL) return 0; @@ -671,13 +699,14 @@ ipv4_doroute(struct rt *rt, struct rt_head *nrs) if ((or = find_route(rt->iface->ctx->ipv4_routes, rt, NULL))) { if (state->added & STATE_FAKE) return 0; - if (or->flags & STATE_FAKE || + if (or->state & STATE_FAKE || or->iface != rt->iface || #ifdef HAVE_ROUTE_METRIC rt->metric != or->metric || #endif rt->src.s_addr != or->src.s_addr || - rt->gate.s_addr != or->gate.s_addr) + rt->gate.s_addr != or->gate.s_addr || + rt->mtu != or->mtu) { if (c_route(or, rt) != 0) return 0; @@ -686,16 +715,20 @@ ipv4_doroute(struct rt *rt, struct rt_head *nrs) free(or); } else { if (state->added & STATE_FAKE) { - or = ipv4_findrt(rt->iface->ctx, rt, 1); - if (or == NULL || - or->gate.s_addr != rt->gate.s_addr) + if ((or = ipv4_findrt(rt->iface->ctx, rt, 1)) == NULL) return 0; + rt->iface = or->iface; + rt->gate.s_addr = or->gate.s_addr; +#ifdef HAVE_ROUTE_METRIC + rt->metric = or->metric; +#endif + rt->mtu = or->mtu; + rt->flags = or->flags; } else { if (n_route(rt) != 0) return 0; } } - rt->flags |= STATE_ADDED; return 1; } diff --git a/ipv4.h b/ipv4.h index 53f94e1c..913aa46d 100644 --- a/ipv4.h +++ b/ipv4.h @@ -61,8 +61,10 @@ struct rt { #ifdef HAVE_ROUTE_METRIC unsigned int metric; #endif + unsigned int mtu; struct in_addr src; unsigned int flags; + unsigned int state; }; TAILQ_HEAD(rt_head, rt); diff --git a/ipv4ll.c b/ipv4ll.c index 564f98f4..4815df4b 100644 --- a/ipv4ll.c +++ b/ipv4ll.c @@ -87,7 +87,7 @@ ipv4ll_subnet_route(const struct interface *ifp) state->addr.s_addr == INADDR_ANY) return NULL; - if ((rt = malloc(sizeof(*rt))) == NULL) { + if ((rt = calloc(1, sizeof(*rt))) == NULL) { logger(ifp->ctx, LOG_ERR, "%s: malloc: %m", __func__); return NULL; } @@ -110,7 +110,7 @@ ipv4ll_default_route(const struct interface *ifp) state->addr.s_addr == INADDR_ANY) return NULL; - if ((rt = malloc(sizeof(*rt))) == NULL) { + if ((rt = calloc(1, sizeof(*rt))) == NULL) { logger(ifp->ctx, LOG_ERR, "%s: malloc: %m", __func__); return NULL; }