From 06f976cad74d027c8080b594eefb391298b4c341 Mon Sep 17 00:00:00 2001 From: Shuai Zhang Date: Sun, 30 Nov 2014 21:03:37 +0800 Subject: [PATCH] audit: added capacity and reserve() to nlmsg MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit There are now two (permitted) ways to add data to netlink message: 1. put_xxx() 2. call nlmsg_reserve() to get a pointer to newly reserved room within the original netlink message, then write or memcpy data to that area. Both of them guarantee adding requested length data do not overflow the pre-allocated message buffer by checking against its cap field first. And there may be no need to access nlmsg_len outside nl module, because both put_xxx() and nlmsg_reserve() have alread did that for us. Signed-off-by: Shuai Zhang Acked-by: Stéphane Graber --- src/lxc/network.c | 291 ++++++++++++++++++++++------------------------ src/lxc/nl.c | 86 ++++++++++---- src/lxc/nl.h | 35 ++++-- 3 files changed, 231 insertions(+), 181 deletions(-) diff --git a/src/lxc/network.c b/src/lxc/network.c index 136e4e06d..ea536161c 100644 --- a/src/lxc/network.c +++ b/src/lxc/network.c @@ -88,26 +88,12 @@ # define IFLA_MACVLAN_MODE 1 #endif -struct link_req { - struct nlmsg nlmsg; - struct ifinfomsg ifinfomsg; -}; - -struct ip_req { - struct nlmsg nlmsg; - struct ifaddrmsg ifa; -}; - -struct rt_req { - struct nlmsg nlmsg; - struct rtmsg rt; -}; int lxc_netdev_move_by_index(int ifindex, pid_t pid, const char* ifname) { struct nl_handler nlh; struct nlmsg *nlmsg = NULL; - struct link_req *link_req; + struct ifinfomsg *ifi; int err; err = netlink_open(&nlh, NETLINK_ROUTE); @@ -119,12 +105,12 @@ int lxc_netdev_move_by_index(int ifindex, pid_t pid, const char* ifname) if (!nlmsg) goto out; - link_req = (struct link_req *)nlmsg; - link_req->ifinfomsg.ifi_family = AF_UNSPEC; - link_req->ifinfomsg.ifi_index = ifindex; - nlmsg->nlmsghdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); - nlmsg->nlmsghdr.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK; - nlmsg->nlmsghdr.nlmsg_type = RTM_NEWLINK; + nlmsg->nlmsghdr->nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK; + nlmsg->nlmsghdr->nlmsg_type = RTM_NEWLINK; + + ifi = nlmsg_reserve(nlmsg, sizeof(struct ifinfomsg)); + ifi->ifi_family = AF_UNSPEC; + ifi->ifi_index = ifindex; if (nla_put_u32(nlmsg, IFLA_NET_NS_PID, pid)) goto out; @@ -269,7 +255,7 @@ int lxc_netdev_delete_by_index(int ifindex) { struct nl_handler nlh; struct nlmsg *nlmsg = NULL, *answer = NULL; - struct link_req *link_req; + struct ifinfomsg *ifi; int err; err = netlink_open(&nlh, NETLINK_ROUTE); @@ -281,16 +267,16 @@ int lxc_netdev_delete_by_index(int ifindex) if (!nlmsg) goto out; - answer = nlmsg_alloc(NLMSG_GOOD_SIZE); + answer = nlmsg_alloc_reserve(NLMSG_GOOD_SIZE); if (!answer) goto out; - link_req = (struct link_req *)nlmsg; - link_req->ifinfomsg.ifi_family = AF_UNSPEC; - link_req->ifinfomsg.ifi_index = ifindex; - nlmsg->nlmsghdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); - nlmsg->nlmsghdr.nlmsg_flags = NLM_F_ACK|NLM_F_REQUEST; - nlmsg->nlmsghdr.nlmsg_type = RTM_DELLINK; + nlmsg->nlmsghdr->nlmsg_flags = NLM_F_ACK|NLM_F_REQUEST; + nlmsg->nlmsghdr->nlmsg_type = RTM_DELLINK; + + ifi = nlmsg_reserve(nlmsg, sizeof(struct ifinfomsg)); + ifi->ifi_family = AF_UNSPEC; + ifi->ifi_index = ifindex; err = netlink_transaction(&nlh, nlmsg, answer); out: @@ -315,7 +301,7 @@ int lxc_netdev_rename_by_index(int ifindex, const char *newname) { struct nl_handler nlh; struct nlmsg *nlmsg = NULL, *answer = NULL; - struct link_req *link_req; + struct ifinfomsg *ifi; int len, err; err = netlink_open(&nlh, NETLINK_ROUTE); @@ -331,16 +317,16 @@ int lxc_netdev_rename_by_index(int ifindex, const char *newname) if (!nlmsg) goto out; - answer = nlmsg_alloc(NLMSG_GOOD_SIZE); + answer = nlmsg_alloc_reserve(NLMSG_GOOD_SIZE); if (!answer) goto out; - link_req = (struct link_req *)nlmsg; - link_req->ifinfomsg.ifi_family = AF_UNSPEC; - link_req->ifinfomsg.ifi_index = ifindex; - nlmsg->nlmsghdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); - nlmsg->nlmsghdr.nlmsg_flags = NLM_F_ACK|NLM_F_REQUEST; - nlmsg->nlmsghdr.nlmsg_type = RTM_NEWLINK; + nlmsg->nlmsghdr->nlmsg_flags = NLM_F_ACK|NLM_F_REQUEST; + nlmsg->nlmsghdr->nlmsg_type = RTM_NEWLINK; + + ifi = nlmsg_reserve(nlmsg, sizeof(struct ifinfomsg)); + ifi->ifi_family = AF_UNSPEC; + ifi->ifi_index = ifindex; if (nla_put_string(nlmsg, IFLA_IFNAME, newname)) goto out; @@ -372,7 +358,7 @@ int netdev_set_flag(const char *name, int flag) { struct nl_handler nlh; struct nlmsg *nlmsg = NULL, *answer = NULL; - struct link_req *link_req; + struct ifinfomsg *ifi; int index, len, err; err = netlink_open(&nlh, NETLINK_ROUTE); @@ -389,7 +375,7 @@ int netdev_set_flag(const char *name, int flag) if (!nlmsg) goto out; - answer = nlmsg_alloc(NLMSG_GOOD_SIZE); + answer = nlmsg_alloc_reserve(NLMSG_GOOD_SIZE); if (!answer) goto out; @@ -398,14 +384,14 @@ int netdev_set_flag(const char *name, int flag) if (!index) goto out; - link_req = (struct link_req *)nlmsg; - link_req->ifinfomsg.ifi_family = AF_UNSPEC; - link_req->ifinfomsg.ifi_index = index; - link_req->ifinfomsg.ifi_change |= IFF_UP; - link_req->ifinfomsg.ifi_flags |= flag; - nlmsg->nlmsghdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); - nlmsg->nlmsghdr.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK; - nlmsg->nlmsghdr.nlmsg_type = RTM_NEWLINK; + nlmsg->nlmsghdr->nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK; + nlmsg->nlmsghdr->nlmsg_type = RTM_NEWLINK; + + ifi = nlmsg_reserve(nlmsg, sizeof(struct ifinfomsg)); + ifi->ifi_family = AF_UNSPEC; + ifi->ifi_index = index; + ifi->ifi_change |= IFF_UP; + ifi->ifi_flags |= flag; err = netlink_transaction(&nlh, nlmsg, answer); out: @@ -419,9 +405,8 @@ int netdev_get_flag(const char* name, int *flag) { struct nl_handler nlh; struct nlmsg *nlmsg = NULL, *answer = NULL; - struct link_req *link_req; - int index, len, err; struct ifinfomsg *ifi; + int index, len, err; if (!name) return -EINVAL; @@ -440,7 +425,7 @@ int netdev_get_flag(const char* name, int *flag) if (!nlmsg) goto out; - answer = nlmsg_alloc(NLMSG_GOOD_SIZE); + answer = nlmsg_alloc_reserve(NLMSG_GOOD_SIZE); if (!answer) goto out; @@ -449,18 +434,18 @@ int netdev_get_flag(const char* name, int *flag) if (!index) goto out; - link_req = (struct link_req *)nlmsg; - link_req->ifinfomsg.ifi_family = AF_UNSPEC; - link_req->ifinfomsg.ifi_index = index; - nlmsg->nlmsghdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); - nlmsg->nlmsghdr.nlmsg_flags = NLM_F_REQUEST; - nlmsg->nlmsghdr.nlmsg_type = RTM_GETLINK; + nlmsg->nlmsghdr->nlmsg_flags = NLM_F_REQUEST; + nlmsg->nlmsghdr->nlmsg_type = RTM_GETLINK; + + ifi = nlmsg_reserve(nlmsg, sizeof(struct ifinfomsg)); + ifi->ifi_family = AF_UNSPEC; + ifi->ifi_index = index; err = netlink_transaction(&nlh, nlmsg, answer); if (err) goto out; - ifi = NLMSG_DATA(answer); + ifi = NLMSG_DATA(answer->nlmsghdr); *flag = ifi->ifi_flags; out: @@ -499,7 +484,7 @@ int netdev_get_mtu(int ifindex) { struct nl_handler nlh; struct nlmsg *nlmsg = NULL, *answer = NULL; - struct ip_req *ip_req; + struct ifinfomsg *ifi; struct nlmsghdr *msg; int err, res; int recv_len = 0, answer_len; @@ -514,21 +499,20 @@ int netdev_get_mtu(int ifindex) if (!nlmsg) goto out; - answer = nlmsg_alloc(NLMSG_GOOD_SIZE); + answer = nlmsg_alloc_reserve(NLMSG_GOOD_SIZE); if (!answer) goto out; /* Save the answer buffer length, since it will be overwritten * on the first receive (and we might need to receive more than * once. */ - answer_len = answer->nlmsghdr.nlmsg_len; + answer_len = answer->nlmsghdr->nlmsg_len; - ip_req = (struct ip_req *)nlmsg; - ip_req->nlmsg.nlmsghdr.nlmsg_len = - NLMSG_LENGTH(sizeof(struct ifaddrmsg)); - ip_req->nlmsg.nlmsghdr.nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP; - ip_req->nlmsg.nlmsghdr.nlmsg_type = RTM_GETLINK; - ip_req->ifa.ifa_family = AF_UNSPEC; + nlmsg->nlmsghdr->nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP; + nlmsg->nlmsghdr->nlmsg_type = RTM_GETLINK; + + ifi = nlmsg_reserve(nlmsg, sizeof(struct ifinfomsg)); + ifi->ifi_family = AF_UNSPEC; /* Send the request for addresses, which returns all addresses * on all interfaces. */ @@ -539,7 +523,7 @@ int netdev_get_mtu(int ifindex) do { /* Restore the answer buffer length, it might have been * overwritten by a previous receive. */ - answer->nlmsghdr.nlmsg_len = answer_len; + answer->nlmsghdr->nlmsg_len = answer_len; /* Get the (next) batch of reply messages */ err = netlink_rcv(&nlh, answer); @@ -550,7 +534,7 @@ int netdev_get_mtu(int ifindex) err = 0; /* Satisfy the typing for the netlink macros */ - msg = &answer->nlmsghdr; + msg = answer->nlmsghdr; while (NLMSG_OK(msg, recv_len)) { @@ -567,7 +551,7 @@ int netdev_get_mtu(int ifindex) break; } - struct ifinfomsg *ifi = NLMSG_DATA(msg); + ifi = NLMSG_DATA(msg); if (ifi->ifi_index == ifindex) { struct rtattr *rta = IFLA_RTA(ifi); int attr_len = msg->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi)); @@ -609,7 +593,7 @@ int lxc_netdev_set_mtu(const char *name, int mtu) { struct nl_handler nlh; struct nlmsg *nlmsg = NULL, *answer = NULL; - struct link_req *link_req; + struct ifinfomsg *ifi; int index, len, err; err = netlink_open(&nlh, NETLINK_ROUTE); @@ -626,7 +610,7 @@ int lxc_netdev_set_mtu(const char *name, int mtu) if (!nlmsg) goto out; - answer = nlmsg_alloc(NLMSG_GOOD_SIZE); + answer = nlmsg_alloc_reserve(NLMSG_GOOD_SIZE); if (!answer) goto out; @@ -635,12 +619,12 @@ int lxc_netdev_set_mtu(const char *name, int mtu) if (!index) goto out; - link_req = (struct link_req *)nlmsg; - link_req->ifinfomsg.ifi_family = AF_UNSPEC; - link_req->ifinfomsg.ifi_index = index; - nlmsg->nlmsghdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); - nlmsg->nlmsghdr.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK; - nlmsg->nlmsghdr.nlmsg_type = RTM_NEWLINK; + nlmsg->nlmsghdr->nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK; + nlmsg->nlmsghdr->nlmsg_type = RTM_NEWLINK; + + ifi = nlmsg_reserve(nlmsg, sizeof(struct ifinfomsg)); + ifi->ifi_family = AF_UNSPEC; + ifi->ifi_index = index; if (nla_put_u32(nlmsg, IFLA_MTU, mtu)) goto out; @@ -667,7 +651,7 @@ int lxc_veth_create(const char *name1, const char *name2) { struct nl_handler nlh; struct nlmsg *nlmsg = NULL, *answer = NULL; - struct link_req *link_req; + struct ifinfomsg *ifi; struct rtattr *nest1, *nest2, *nest3; int len, err; @@ -689,16 +673,16 @@ int lxc_veth_create(const char *name1, const char *name2) if (!nlmsg) goto out; - answer = nlmsg_alloc(NLMSG_GOOD_SIZE); + answer = nlmsg_alloc_reserve(NLMSG_GOOD_SIZE); if (!answer) goto out; - link_req = (struct link_req *)nlmsg; - link_req->ifinfomsg.ifi_family = AF_UNSPEC; - nlmsg->nlmsghdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); - nlmsg->nlmsghdr.nlmsg_flags = + nlmsg->nlmsghdr->nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL|NLM_F_ACK; - nlmsg->nlmsghdr.nlmsg_type = RTM_NEWLINK; + nlmsg->nlmsghdr->nlmsg_type = RTM_NEWLINK; + + ifi = nlmsg_reserve(nlmsg, sizeof(struct ifinfomsg)); + ifi->ifi_family = AF_UNSPEC; err = -EINVAL; nest1 = nla_begin_nested(nlmsg, IFLA_LINKINFO); @@ -716,7 +700,9 @@ int lxc_veth_create(const char *name1, const char *name2) if (!nest3) goto out; - nlmsg->nlmsghdr.nlmsg_len += sizeof(struct ifinfomsg); + ifi = nlmsg_reserve(nlmsg, sizeof(struct ifinfomsg)); + if (!ifi) + goto out; if (nla_put_string(nlmsg, IFLA_IFNAME, name2)) goto out; @@ -743,7 +729,7 @@ int lxc_vlan_create(const char *master, const char *name, unsigned short vlanid) { struct nl_handler nlh; struct nlmsg *nlmsg = NULL, *answer = NULL; - struct link_req *link_req; + struct ifinfomsg *ifi; struct rtattr *nest, *nest2; int lindex, len, err; @@ -765,7 +751,7 @@ int lxc_vlan_create(const char *master, const char *name, unsigned short vlanid) if (!nlmsg) goto err3; - answer = nlmsg_alloc(NLMSG_GOOD_SIZE); + answer = nlmsg_alloc_reserve(NLMSG_GOOD_SIZE); if (!answer) goto err2; @@ -774,12 +760,12 @@ int lxc_vlan_create(const char *master, const char *name, unsigned short vlanid) if (!lindex) goto err1; - link_req = (struct link_req *)nlmsg; - link_req->ifinfomsg.ifi_family = AF_UNSPEC; - nlmsg->nlmsghdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); - nlmsg->nlmsghdr.nlmsg_flags = + nlmsg->nlmsghdr->nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL|NLM_F_ACK; - nlmsg->nlmsghdr.nlmsg_type = RTM_NEWLINK; + nlmsg->nlmsghdr->nlmsg_type = RTM_NEWLINK; + + ifi = nlmsg_reserve(nlmsg, sizeof(struct ifinfomsg)); + ifi->ifi_family = AF_UNSPEC; nest = nla_begin_nested(nlmsg, IFLA_LINKINFO); if (!nest) @@ -819,7 +805,7 @@ int lxc_macvlan_create(const char *master, const char *name, int mode) { struct nl_handler nlh; struct nlmsg *nlmsg = NULL, *answer = NULL; - struct link_req *link_req; + struct ifinfomsg *ifi; struct rtattr *nest, *nest2; int index, len, err; @@ -841,7 +827,7 @@ int lxc_macvlan_create(const char *master, const char *name, int mode) if (!nlmsg) goto out; - answer = nlmsg_alloc(NLMSG_GOOD_SIZE); + answer = nlmsg_alloc_reserve(NLMSG_GOOD_SIZE); if (!answer) goto out; @@ -850,12 +836,12 @@ int lxc_macvlan_create(const char *master, const char *name, int mode) if (!index) goto out; - link_req = (struct link_req *)nlmsg; - link_req->ifinfomsg.ifi_family = AF_UNSPEC; - nlmsg->nlmsghdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); - nlmsg->nlmsghdr.nlmsg_flags = + nlmsg->nlmsghdr->nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL|NLM_F_ACK; - nlmsg->nlmsghdr.nlmsg_type = RTM_NEWLINK; + nlmsg->nlmsghdr->nlmsg_type = RTM_NEWLINK; + + ifi = nlmsg_reserve(nlmsg, sizeof(struct ifinfomsg)); + ifi->ifi_family = AF_UNSPEC; nest = nla_begin_nested(nlmsg, IFLA_LINKINFO); if (!nest) @@ -1011,7 +997,7 @@ static int ip_addr_add(int family, int ifindex, { struct nl_handler nlh; struct nlmsg *nlmsg = NULL, *answer = NULL; - struct ip_req *ip_req; + struct ifaddrmsg *ifa; int addrlen; int err; @@ -1027,20 +1013,19 @@ static int ip_addr_add(int family, int ifindex, if (!nlmsg) goto out; - answer = nlmsg_alloc(NLMSG_GOOD_SIZE); + answer = nlmsg_alloc_reserve(NLMSG_GOOD_SIZE); if (!answer) goto out; - ip_req = (struct ip_req *)nlmsg; - ip_req->nlmsg.nlmsghdr.nlmsg_len = - NLMSG_LENGTH(sizeof(struct ifaddrmsg)); - ip_req->nlmsg.nlmsghdr.nlmsg_flags = + nlmsg->nlmsghdr->nlmsg_flags = NLM_F_ACK|NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL; - ip_req->nlmsg.nlmsghdr.nlmsg_type = RTM_NEWADDR; - ip_req->ifa.ifa_prefixlen = prefix; - ip_req->ifa.ifa_index = ifindex; - ip_req->ifa.ifa_family = family; - ip_req->ifa.ifa_scope = 0; + nlmsg->nlmsghdr->nlmsg_type = RTM_NEWADDR; + + ifa = nlmsg_reserve(nlmsg, sizeof(struct ifaddrmsg)); + ifa->ifa_prefixlen = prefix; + ifa->ifa_index = ifindex; + ifa->ifa_family = family; + ifa->ifa_scope = 0; err = -EINVAL; if (nla_put_buffer(nlmsg, IFA_LOCAL, addr, addrlen)) @@ -1085,12 +1070,13 @@ int lxc_ipv4_addr_add(int ifindex, struct in_addr *addr, * address and stores that pointer in *res (so res should be an * in_addr** or in6_addr**). */ -static int ifa_get_local_ip(int family, struct ip_req *ip_info, void** res) { - struct rtattr *rta = IFA_RTA(&ip_info->ifa); - int attr_len = IFA_PAYLOAD(&ip_info->nlmsg.nlmsghdr); +static int ifa_get_local_ip(int family, struct nlmsghdr *msg, void** res) { + struct ifaddrmsg *ifa = NLMSG_DATA(msg); + struct rtattr *rta = IFA_RTA(ifa); + int attr_len = NLMSG_PAYLOAD(msg, sizeof(struct ifaddrmsg)); int addrlen; - if (ip_info->ifa.ifa_family != family) + if (ifa->ifa_family != family) return 0; addrlen = family == AF_INET ? sizeof(struct in_addr) : @@ -1129,7 +1115,7 @@ static int ip_addr_get(int family, int ifindex, void **res) { struct nl_handler nlh; struct nlmsg *nlmsg = NULL, *answer = NULL; - struct ip_req *ip_req, *ip_info; + struct ifaddrmsg *ifa; struct nlmsghdr *msg; int err; int recv_len = 0, answer_len; @@ -1144,21 +1130,20 @@ static int ip_addr_get(int family, int ifindex, void **res) if (!nlmsg) goto out; - answer = nlmsg_alloc(NLMSG_GOOD_SIZE); + answer = nlmsg_alloc_reserve(NLMSG_GOOD_SIZE); if (!answer) goto out; /* Save the answer buffer length, since it will be overwritten * on the first receive (and we might need to receive more than * once. */ - answer_len = answer->nlmsghdr.nlmsg_len; + answer_len = answer->nlmsghdr->nlmsg_len; + + nlmsg->nlmsghdr->nlmsg_flags = NLM_F_REQUEST|NLM_F_ROOT; + nlmsg->nlmsghdr->nlmsg_type = RTM_GETADDR; - ip_req = (struct ip_req *)nlmsg; - ip_req->nlmsg.nlmsghdr.nlmsg_len = - NLMSG_LENGTH(sizeof(struct ifaddrmsg)); - ip_req->nlmsg.nlmsghdr.nlmsg_flags = NLM_F_REQUEST|NLM_F_ROOT; - ip_req->nlmsg.nlmsghdr.nlmsg_type = RTM_GETADDR; - ip_req->ifa.ifa_family = family; + ifa = nlmsg_reserve(nlmsg, sizeof(struct ifaddrmsg)); + ifa->ifa_family = family; /* Send the request for addresses, which returns all addresses * on all interfaces. */ @@ -1169,7 +1154,7 @@ static int ip_addr_get(int family, int ifindex, void **res) do { /* Restore the answer buffer length, it might have been * overwritten by a previous receive. */ - answer->nlmsghdr.nlmsg_len = answer_len; + answer->nlmsghdr->nlmsg_len = answer_len; /* Get the (next) batch of reply messages */ err = netlink_rcv(&nlh, answer); @@ -1180,7 +1165,7 @@ static int ip_addr_get(int family, int ifindex, void **res) err = 0; /* Satisfy the typing for the netlink macros */ - msg = &answer->nlmsghdr; + msg = answer->nlmsghdr; while (NLMSG_OK(msg, recv_len)) { /* Stop reading if we see an error message */ @@ -1201,9 +1186,9 @@ static int ip_addr_get(int family, int ifindex, void **res) goto out; } - ip_info = (struct ip_req *)msg; - if (ip_info->ifa.ifa_index == ifindex) { - if (ifa_get_local_ip(family, ip_info, res) < 0) { + ifa = (struct ifaddrmsg *)NLMSG_DATA(msg); + if (ifa->ifa_index == ifindex) { + if (ifa_get_local_ip(family, msg, res) < 0) { err = -1; goto out; } @@ -1247,7 +1232,7 @@ static int ip_gateway_add(int family, int ifindex, void *gw) { struct nl_handler nlh; struct nlmsg *nlmsg = NULL, *answer = NULL; - struct rt_req *rt_req; + struct rtmsg *rt; int addrlen; int err; @@ -1263,23 +1248,22 @@ static int ip_gateway_add(int family, int ifindex, void *gw) if (!nlmsg) goto out; - answer = nlmsg_alloc(NLMSG_GOOD_SIZE); + answer = nlmsg_alloc_reserve(NLMSG_GOOD_SIZE); if (!answer) goto out; - rt_req = (struct rt_req *)nlmsg; - rt_req->nlmsg.nlmsghdr.nlmsg_len = - NLMSG_LENGTH(sizeof(struct rtmsg)); - rt_req->nlmsg.nlmsghdr.nlmsg_flags = + nlmsg->nlmsghdr->nlmsg_flags = NLM_F_ACK|NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL; - rt_req->nlmsg.nlmsghdr.nlmsg_type = RTM_NEWROUTE; - rt_req->rt.rtm_family = family; - rt_req->rt.rtm_table = RT_TABLE_MAIN; - rt_req->rt.rtm_scope = RT_SCOPE_UNIVERSE; - rt_req->rt.rtm_protocol = RTPROT_BOOT; - rt_req->rt.rtm_type = RTN_UNICAST; + nlmsg->nlmsghdr->nlmsg_type = RTM_NEWROUTE; + + rt = nlmsg_reserve(nlmsg, sizeof(struct rtmsg)); + rt->rtm_family = family; + rt->rtm_table = RT_TABLE_MAIN; + rt->rtm_scope = RT_SCOPE_UNIVERSE; + rt->rtm_protocol = RTPROT_BOOT; + rt->rtm_type = RTN_UNICAST; /* "default" destination */ - rt_req->rt.rtm_dst_len = 0; + rt->rtm_dst_len = 0; err = -EINVAL; if (nla_put_buffer(nlmsg, RTA_GATEWAY, gw, addrlen)) @@ -1312,7 +1296,7 @@ static int ip_route_dest_add(int family, int ifindex, void *dest) { struct nl_handler nlh; struct nlmsg *nlmsg = NULL, *answer = NULL; - struct rt_req *rt_req; + struct rtmsg *rt; int addrlen; int err; @@ -1328,22 +1312,21 @@ static int ip_route_dest_add(int family, int ifindex, void *dest) if (!nlmsg) goto out; - answer = nlmsg_alloc(NLMSG_GOOD_SIZE); + answer = nlmsg_alloc_reserve(NLMSG_GOOD_SIZE); if (!answer) goto out; - rt_req = (struct rt_req *)nlmsg; - rt_req->nlmsg.nlmsghdr.nlmsg_len = - NLMSG_LENGTH(sizeof(struct rtmsg)); - rt_req->nlmsg.nlmsghdr.nlmsg_flags = + nlmsg->nlmsghdr->nlmsg_flags = NLM_F_ACK|NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL; - rt_req->nlmsg.nlmsghdr.nlmsg_type = RTM_NEWROUTE; - rt_req->rt.rtm_family = family; - rt_req->rt.rtm_table = RT_TABLE_MAIN; - rt_req->rt.rtm_scope = RT_SCOPE_LINK; - rt_req->rt.rtm_protocol = RTPROT_BOOT; - rt_req->rt.rtm_type = RTN_UNICAST; - rt_req->rt.rtm_dst_len = addrlen*8; + nlmsg->nlmsghdr->nlmsg_type = RTM_NEWROUTE; + + rt = nlmsg_reserve(nlmsg, sizeof(struct rtmsg)); + rt->rtm_family = family; + rt->rtm_table = RT_TABLE_MAIN; + rt->rtm_scope = RT_SCOPE_LINK; + rt->rtm_protocol = RTPROT_BOOT; + rt->rtm_type = RTN_UNICAST; + rt->rtm_dst_len = addrlen*8; err = -EINVAL; if (nla_put_buffer(nlmsg, RTA_DST, dest, addrlen)) diff --git a/src/lxc/nl.c b/src/lxc/nl.c index 132a907f7..dd044f0f7 100644 --- a/src/lxc/nl.c +++ b/src/lxc/nl.c @@ -37,12 +37,12 @@ extern size_t nlmsg_len(const struct nlmsg *nlmsg) { - return nlmsg->nlmsghdr.nlmsg_len - NLMSG_HDRLEN; + return nlmsg->nlmsghdr->nlmsg_len - NLMSG_HDRLEN; } extern void *nlmsg_data(struct nlmsg *nlmsg) { - char *data = ((char *)nlmsg) + NLMSG_ALIGN(sizeof(struct nlmsghdr)); + char *data = ((char *)nlmsg) + NLMSG_HDRLEN; if (!nlmsg_len(nlmsg)) return NULL; return data; @@ -53,13 +53,16 @@ static int nla_put(struct nlmsg *nlmsg, int attr, { struct rtattr *rta; size_t rtalen = RTA_LENGTH(len); + size_t tlen = NLMSG_ALIGN(nlmsg->nlmsghdr->nlmsg_len) + RTA_ALIGN(rtalen); - rta = NLMSG_TAIL(&nlmsg->nlmsghdr); - rta->rta_type = attr; - rta->rta_len = rtalen; - memcpy(RTA_DATA(rta), data, len); - nlmsg->nlmsghdr.nlmsg_len = - NLMSG_ALIGN(nlmsg->nlmsghdr.nlmsg_len) + RTA_ALIGN(rtalen); + if (tlen > nlmsg->cap) + return -ENOMEM; + + rta = NLMSG_TAIL(nlmsg->nlmsghdr); + rta->rta_type = attr; + rta->rta_len = rtalen; + memcpy(RTA_DATA(rta), data, len); + nlmsg->nlmsghdr->nlmsg_len = tlen; return 0; } @@ -91,7 +94,7 @@ extern int nla_put_attr(struct nlmsg *nlmsg, int attr) struct rtattr *nla_begin_nested(struct nlmsg *nlmsg, int attr) { - struct rtattr *rtattr = NLMSG_TAIL(&nlmsg->nlmsghdr); + struct rtattr *rtattr = NLMSG_TAIL(nlmsg->nlmsghdr); if (nla_put_attr(nlmsg, attr)) return NULL; @@ -101,7 +104,7 @@ struct rtattr *nla_begin_nested(struct nlmsg *nlmsg, int attr) void nla_end_nested(struct nlmsg *nlmsg, struct rtattr *attr) { - attr->rta_len = (void *)NLMSG_TAIL(&nlmsg->nlmsghdr) - (void *)attr; + attr->rta_len = (void *)NLMSG_TAIL(nlmsg->nlmsghdr) - (void *)attr; } extern struct nlmsg *nlmsg_alloc(size_t size) @@ -109,18 +112,61 @@ extern struct nlmsg *nlmsg_alloc(size_t size) struct nlmsg *nlmsg; size_t len = NLMSG_HDRLEN + NLMSG_ALIGN(size); - nlmsg = (struct nlmsg *)malloc(len); + nlmsg = (struct nlmsg *)malloc(sizeof(struct nlmsg)); if (!nlmsg) return NULL; - memset(nlmsg, 0, len); - nlmsg->nlmsghdr.nlmsg_len = NLMSG_HDRLEN; + nlmsg->nlmsghdr = (struct nlmsghdr *)malloc(len); + if (!nlmsg->nlmsghdr) + goto errout; + + memset(nlmsg->nlmsghdr, 0, len); + nlmsg->cap = len; + nlmsg->nlmsghdr->nlmsg_len = NLMSG_HDRLEN; + return nlmsg; +errout: + free(nlmsg); + return NULL; +} + +extern void *nlmsg_reserve(struct nlmsg *nlmsg, size_t len) +{ + void *buf; + size_t nlmsg_len = nlmsg->nlmsghdr->nlmsg_len; + size_t tlen = NLMSG_ALIGN(len); + + if (nlmsg_len + tlen > nlmsg->cap) + return NULL; + + buf = ((char *)(nlmsg->nlmsghdr)) + nlmsg_len; + nlmsg->nlmsghdr->nlmsg_len += tlen; + + if (tlen > len) + memset(buf + len, 0, tlen - len); + + return buf; +} + +extern struct nlmsg *nlmsg_alloc_reserve(size_t size) +{ + struct nlmsg *nlmsg; + + nlmsg = nlmsg_alloc(size); + if (!nlmsg) + return NULL; + + // just set message length to cap directly + nlmsg->nlmsghdr->nlmsg_len = nlmsg->cap; return nlmsg; } extern void nlmsg_free(struct nlmsg *nlmsg) { + if (!nlmsg) + return; + + free(nlmsg->nlmsghdr); free(nlmsg); } @@ -129,8 +175,8 @@ extern int netlink_rcv(struct nl_handler *handler, struct nlmsg *answer) int ret; struct sockaddr_nl nladdr; struct iovec iov = { - .iov_base = answer, - .iov_len = answer->nlmsghdr.nlmsg_len, + .iov_base = answer->nlmsghdr, + .iov_len = answer->nlmsghdr->nlmsg_len, }; struct msghdr msg = { @@ -157,7 +203,7 @@ again: return 0; if (msg.msg_flags & MSG_TRUNC && - ret == answer->nlmsghdr.nlmsg_len) + ret == answer->nlmsghdr->nlmsg_len) return -EMSGSIZE; return ret; @@ -167,8 +213,8 @@ extern int netlink_send(struct nl_handler *handler, struct nlmsg *nlmsg) { struct sockaddr_nl nladdr; struct iovec iov = { - .iov_base = (void*)nlmsg, - .iov_len = nlmsg->nlmsghdr.nlmsg_len, + .iov_base = nlmsg->nlmsghdr, + .iov_len = nlmsg->nlmsghdr->nlmsg_len, }; struct msghdr msg = { .msg_name = &nladdr, @@ -206,8 +252,8 @@ extern int netlink_transaction(struct nl_handler *handler, if (ret < 0) return ret; - if (answer->nlmsghdr.nlmsg_type == NLMSG_ERROR) { - struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(answer); + if (answer->nlmsghdr->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(answer->nlmsghdr); return err->error; } diff --git a/src/lxc/nl.h b/src/lxc/nl.h index 8e737fc8b..f6f2fd0bf 100644 --- a/src/lxc/nl.h +++ b/src/lxc/nl.h @@ -53,14 +53,16 @@ struct nl_handler { }; /* - * struct nlmsg : the netlink message structure, it consists just - * on a definition for a nlmsghdr. This message is to be used to + * struct nlmsg : the netlink message structure. This message is to be used to * be allocated with netlink_alloc. - * @nlmsghdr : a pointer to a netlink message header, this field - * _must_ be always the first field of this structure + * + * @nlmsghdr: a pointer to a netlink message header + * @cap: capacity of the netlink message, this is the initially allocated size + * and later operations (e.g. reserve and put) can not exceed this limit. */ struct nlmsg { - struct nlmsghdr nlmsghdr; + struct nlmsghdr *nlmsghdr; + ssize_t cap; }; /* @@ -210,15 +212,34 @@ void nla_end_nested(struct nlmsg *nlmsg, struct rtattr *attr); * is sizeof(header) + sizeof(padding) + payloadsize + sizeof(padding), * in other words, the function will allocate more than specified. When * the buffer is allocated, the content is zeroed. - * The function will also fill the field nlmsg_len with computed size. + * The function will also fill the field nlmsg_len with NLMSG_HDRLEN. * If the allocation must be for the specified size, just use malloc. * - * @size: the size of the payload to be allocated + * @size: the capacity of the payload to be allocated * * Returns a pointer to the newly allocated netlink message, NULL otherwise */ struct nlmsg *nlmsg_alloc(size_t size); +/* + * nlmsg_alloc_reserve: like nlmsg_alloc(), but reserve the whole payload + * after allocated, that is, the field nlmsg_len be set to the capacity + * of nlmsg. Often used to allocate a message for the reply. + * + * @size: the capacity of the payload to be allocated. + */ +struct nlmsg *nlmsg_alloc_reserve(size_t size); + +/* + * Reserve room for additional data at the tail of a netlink message + * + * @nlmsg: the netlink message + * @len: length of additional data to reserve room for + * + * Returns a pointer to newly reserved room or NULL + */ +void *nlmsg_reserve(struct nlmsg *nlmsg, size_t len); + /* * nlmsg_free : free a previously allocate message * -- 2.39.2