summaryrefslogtreecommitdiff
path: root/ip/iproute.c
diff options
context:
space:
mode:
authorPhil Sutter <phil@nwl.cc>2018-09-06 15:31:51 +0200
committerStephen Hemminger <stephen@networkplumber.org>2018-09-10 12:14:50 -0700
commitbd59e5b1517b09b6f26d59f38fe6077d953c2396 (patch)
tree2da95580aafab2d63b26ef32adf93a17a60e743d /ip/iproute.c
parent40c2916fda23bc9bd124009934544d3c0fddd1c0 (diff)
ip-route: Fix segfault with many nexthops
It was possible to crash ip-route by adding an IPv6 route with 37 nexthop statements. A simple reproducer is: | for i in `seq 37`; do | nhs="nexthop via 1111::$i "$nhs | done | ip -6 route add 3333::/64 $nhs The related code was broken in multiple ways: * parse_one_nh() assumed that rta points to 4kB of storage but caller provided just 1kB. Fixed by passing 'len' parameter with the correct value. * Error checking of rta_addattr*() calls in parse_one_nh() and called functions was completely absent, so with above fix in place output flood would occur due to parser looping forever. While being at it, increase message buffer sizes to 4k. This allows for at most 144 nexthops. Signed-off-by: Phil Sutter <phil@nwl.cc> Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
Diffstat (limited to 'ip/iproute.c')
-rw-r--r--ip/iproute.c43
1 files changed, 26 insertions, 17 deletions
diff --git a/ip/iproute.c b/ip/iproute.c
index 30833414..398322fd 100644
--- a/ip/iproute.c
+++ b/ip/iproute.c
@@ -941,7 +941,7 @@ int print_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
}
static int parse_one_nh(struct nlmsghdr *n, struct rtmsg *r,
- struct rtattr *rta, struct rtnexthop *rtnh,
+ struct rtattr *rta, size_t len, struct rtnexthop *rtnh,
int *argcp, char ***argvp)
{
int argc = *argcp;
@@ -962,11 +962,16 @@ static int parse_one_nh(struct nlmsghdr *n, struct rtmsg *r,
if (r->rtm_family == AF_UNSPEC)
r->rtm_family = addr.family;
if (addr.family == r->rtm_family) {
- rta_addattr_l(rta, 4096, RTA_GATEWAY, &addr.data, addr.bytelen);
- rtnh->rtnh_len += sizeof(struct rtattr) + addr.bytelen;
+ if (rta_addattr_l(rta, len, RTA_GATEWAY,
+ &addr.data, addr.bytelen))
+ return -1;
+ rtnh->rtnh_len += sizeof(struct rtattr)
+ + addr.bytelen;
} else {
- rta_addattr_l(rta, 4096, RTA_VIA, &addr.family, addr.bytelen+2);
- rtnh->rtnh_len += RTA_SPACE(addr.bytelen+2);
+ if (rta_addattr_l(rta, len, RTA_VIA,
+ &addr.family, addr.bytelen + 2))
+ return -1;
+ rtnh->rtnh_len += RTA_SPACE(addr.bytelen + 2);
}
} else if (strcmp(*argv, "dev") == 0) {
NEXT_ARG();
@@ -988,13 +993,15 @@ static int parse_one_nh(struct nlmsghdr *n, struct rtmsg *r,
NEXT_ARG();
if (get_rt_realms_or_raw(&realm, *argv))
invarg("\"realm\" value is invalid\n", *argv);
- rta_addattr32(rta, 4096, RTA_FLOW, realm);
+ if (rta_addattr32(rta, len, RTA_FLOW, realm))
+ return -1;
rtnh->rtnh_len += sizeof(struct rtattr) + 4;
} else if (strcmp(*argv, "encap") == 0) {
- int len = rta->rta_len;
+ int old_len = rta->rta_len;
- lwt_parse_encap(rta, 4096, &argc, &argv);
- rtnh->rtnh_len += rta->rta_len - len;
+ if (lwt_parse_encap(rta, len, &argc, &argv))
+ return -1;
+ rtnh->rtnh_len += rta->rta_len - old_len;
} else if (strcmp(*argv, "as") == 0) {
inet_prefix addr;
@@ -1002,8 +1009,9 @@ static int parse_one_nh(struct nlmsghdr *n, struct rtmsg *r,
if (strcmp(*argv, "to") == 0)
NEXT_ARG();
get_addr(&addr, *argv, r->rtm_family);
- rta_addattr_l(rta, 4096, RTA_NEWDST, &addr.data,
- addr.bytelen);
+ if (rta_addattr_l(rta, len, RTA_NEWDST,
+ &addr.data, addr.bytelen))
+ return -1;
rtnh->rtnh_len += sizeof(struct rtattr) + addr.bytelen;
} else
break;
@@ -1016,7 +1024,7 @@ static int parse_one_nh(struct nlmsghdr *n, struct rtmsg *r,
static int parse_nexthops(struct nlmsghdr *n, struct rtmsg *r,
int argc, char **argv)
{
- char buf[1024];
+ char buf[4096];
struct rtattr *rta = (void *)buf;
struct rtnexthop *rtnh;
@@ -1036,7 +1044,7 @@ static int parse_nexthops(struct nlmsghdr *n, struct rtmsg *r,
memset(rtnh, 0, sizeof(*rtnh));
rtnh->rtnh_len = sizeof(*rtnh);
rta->rta_len += rtnh->rtnh_len;
- if (parse_one_nh(n, r, rta, rtnh, &argc, &argv)) {
+ if (parse_one_nh(n, r, rta, 4096, rtnh, &argc, &argv)) {
fprintf(stderr, "Error: cannot parse nexthop\n");
exit(-1);
}
@@ -1044,7 +1052,8 @@ static int parse_nexthops(struct nlmsghdr *n, struct rtmsg *r,
}
if (rta->rta_len > RTA_LENGTH(0))
- addattr_l(n, 1024, RTA_MULTIPATH, RTA_DATA(rta), RTA_PAYLOAD(rta));
+ return addattr_l(n, 4096, RTA_MULTIPATH,
+ RTA_DATA(rta), RTA_PAYLOAD(rta));
return 0;
}
@@ -1053,7 +1062,7 @@ static int iproute_modify(int cmd, unsigned int flags, int argc, char **argv)
struct {
struct nlmsghdr n;
struct rtmsg r;
- char buf[1024];
+ char buf[4096];
} req = {
.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)),
.n.nlmsg_flags = NLM_F_REQUEST | flags,
@@ -1484,8 +1493,8 @@ static int iproute_modify(int cmd, unsigned int flags, int argc, char **argv)
addattr_l(&req.n, sizeof(req), RTA_METRICS, RTA_DATA(mxrta), RTA_PAYLOAD(mxrta));
}
- if (nhs_ok)
- parse_nexthops(&req.n, &req.r, argc, argv);
+ if (nhs_ok && parse_nexthops(&req.n, &req.r, argc, argv))
+ return -1;
if (req.r.rtm_family == AF_UNSPEC)
req.r.rtm_family = AF_INET;