From 9de62e7b75654bcf23c52a13d451c9b995e2a659 Mon Sep 17 00:00:00 2001 From: "Nicholas J. Kain" Date: Sun, 16 Mar 2014 18:07:09 -0400 Subject: [PATCH] Use netlink when setting the link MTU. --- ndhc/ifset.c | 81 ++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 63 insertions(+), 18 deletions(-) diff --git a/ndhc/ifset.c b/ndhc/ifset.c index 23316fc..5b6fe84 100644 --- a/ndhc/ifset.c +++ b/ndhc/ifset.c @@ -30,6 +30,7 @@ #include #include #include +#include #define __USE_GNU 1 #include #include @@ -161,6 +162,35 @@ static ssize_t rtnl_if_flags_send(int fd, int type, int ifi_flags) return rtnl_do_send(fd, request, header->nlmsg_len, __func__); } +static ssize_t rtnl_if_mtu_set(int fd, unsigned int mtu) +{ + uint8_t request[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + + NLMSG_ALIGN(sizeof(struct ifinfomsg)) + + RTA_LENGTH(sizeof(uint32_t))]; + struct nlmsghdr *header; + struct ifinfomsg *ifinfomsg; + + memset(&request, 0, sizeof request); + header = (struct nlmsghdr *)request; + header->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + header->nlmsg_type = RTM_SETLINK; + header->nlmsg_flags = NLM_F_REPLACE | NLM_F_ACK | NLM_F_REQUEST; + header->nlmsg_seq = ifset_nl_seq++; + + ifinfomsg = NLMSG_DATA(header); + ifinfomsg->ifi_index = client_config.ifindex; + ifinfomsg->ifi_change = 0xffffffff; + + if (nl_add_rtattr(header, sizeof request, IFLA_MTU, + &mtu, sizeof mtu) < 0) { + log_line("%s: (%s) couldn't add IFLA_MTU to nlmsg", + client_config.interface, __func__); + return -1; + } + + return rtnl_do_send(fd, request, header->nlmsg_len, __func__); +} + static ssize_t rtnl_addr_broadcast_send(int fd, int type, int ifa_flags, int ifa_scope, uint32_t *ipaddr, uint32_t *bcast, uint8_t prefixlen) @@ -497,31 +527,46 @@ void perform_router(const char *str, size_t len) void perform_mtu(const char *str, size_t len) { - int fd; - unsigned int mtu; - struct ifreq ifrt; - if (!str) return; if (len < 2) return; - mtu = strtol(str, NULL, 10); - // Minimum MTU for physical IPv4 links is 576 octets. - if (mtu < 576) - return; - ifrt.ifr_mtu = mtu; - strnkcpy(ifrt.ifr_name, client_config.interface, IFNAMSIZ); - - fd = socket(AF_INET, SOCK_DGRAM, 0); - if (fd == -1) { - log_line("%s: (perform_mtu) failed to open interface socket: %s", - client_config.interface, strerror(errno)); + char *estr; + long tmtu = strtol(str, &estr, 10); + if (estr == str) { + log_line("%s: (%s) provided mtu arg isn't a valid number", + client_config.interface, __func__); return; } - if (ioctl(fd, SIOCSIFMTU, &ifrt) < 0) - log_line("%s: failed to set MTU (%d): %s", client_config.interface, mtu, - strerror(errno)); + if ((tmtu == LONG_MAX || tmtu == LONG_MIN) && errno == ERANGE) { + log_line("%s: (%s) provided mtu arg would overflow a long", + client_config.interface, __func__); + return; + } + if (tmtu > UINT_MAX) { + log_line("%s: (%s) provided mtu arg would overflow unsigned int", + client_config.interface, __func__); + return; + } + // 68 bytes for IPv4. 1280 bytes for IPv6. + if (tmtu < 68) { + log_line("%s: (%s) provided mtu arg (%ul) less than minimum MTU (68)", + client_config.interface, __func__, tmtu); + return; + } + unsigned int mtu = (unsigned int)tmtu; + + int fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (fd < 0) { + log_line("%s: (%s) netlink socket open failed: %s", + client_config.interface, __func__, strerror(errno)); + return; + } + + if (rtnl_if_mtu_set(fd, mtu) < 0) + log_line("%s: (%s) failed to set MTU [%d]", + client_config.interface, __func__, mtu); else log_line("MTU set to: '%s'", str); close(fd);