[BRIDGE] netlink: Convert bridge netlink code to new netlink interface

Removes dependency on buggy rta_buf, fixes a memory corruption bug due to
a unvalidated netlink attribute, and simplifies the code.

Signed-off-by: Thomas Graf <tgraf@suug.ch>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Thomas Graf 2006-11-20 16:20:22 -08:00 committed by David S. Miller
parent 82e3ab9dbe
commit 746859625d

View file

@ -36,51 +36,43 @@ static int br_fill_ifinfo(struct sk_buff *skb, const struct net_bridge_port *por
{ {
const struct net_bridge *br = port->br; const struct net_bridge *br = port->br;
const struct net_device *dev = port->dev; const struct net_device *dev = port->dev;
struct ifinfomsg *r; struct ifinfomsg *hdr;
struct nlmsghdr *nlh; struct nlmsghdr *nlh;
unsigned char *b = skb->tail;
u32 mtu = dev->mtu;
u8 operstate = netif_running(dev) ? dev->operstate : IF_OPER_DOWN; u8 operstate = netif_running(dev) ? dev->operstate : IF_OPER_DOWN;
u8 portstate = port->state;
pr_debug("br_fill_info event %d port %s master %s\n", pr_debug("br_fill_info event %d port %s master %s\n",
event, dev->name, br->dev->name); event, dev->name, br->dev->name);
nlh = NLMSG_NEW(skb, pid, seq, event, sizeof(*r), flags); nlh = nlmsg_put(skb, pid, seq, event, sizeof(*hdr), flags);
r = NLMSG_DATA(nlh); if (nlh == NULL)
r->ifi_family = AF_BRIDGE; return -ENOBUFS;
r->__ifi_pad = 0;
r->ifi_type = dev->type;
r->ifi_index = dev->ifindex;
r->ifi_flags = dev_get_flags(dev);
r->ifi_change = 0;
RTA_PUT(skb, IFLA_IFNAME, strlen(dev->name)+1, dev->name); hdr = nlmsg_data(nlh);
hdr->ifi_family = AF_BRIDGE;
hdr->__ifi_pad = 0;
hdr->ifi_type = dev->type;
hdr->ifi_index = dev->ifindex;
hdr->ifi_flags = dev_get_flags(dev);
hdr->ifi_change = 0;
RTA_PUT(skb, IFLA_MASTER, sizeof(int), &br->dev->ifindex); NLA_PUT_STRING(skb, IFLA_IFNAME, dev->name);
NLA_PUT_U32(skb, IFLA_MASTER, br->dev->ifindex);
NLA_PUT_U32(skb, IFLA_MTU, dev->mtu);
NLA_PUT_U8(skb, IFLA_OPERSTATE, operstate);
if (dev->addr_len) if (dev->addr_len)
RTA_PUT(skb, IFLA_ADDRESS, dev->addr_len, dev->dev_addr); NLA_PUT(skb, IFLA_ADDRESS, dev->addr_len, dev->dev_addr);
RTA_PUT(skb, IFLA_MTU, sizeof(mtu), &mtu);
if (dev->ifindex != dev->iflink) if (dev->ifindex != dev->iflink)
RTA_PUT(skb, IFLA_LINK, sizeof(int), &dev->iflink); NLA_PUT_U32(skb, IFLA_LINK, dev->iflink);
RTA_PUT(skb, IFLA_OPERSTATE, sizeof(operstate), &operstate);
if (event == RTM_NEWLINK) if (event == RTM_NEWLINK)
RTA_PUT(skb, IFLA_PROTINFO, sizeof(portstate), &portstate); NLA_PUT_U8(skb, IFLA_PROTINFO, port->state);
nlh->nlmsg_len = skb->tail - b; return nlmsg_end(skb, nlh);
return skb->len; nla_put_failure:
return nlmsg_cancel(skb, nlh);
nlmsg_failure:
rtattr_failure:
skb_trim(skb, b - skb->data);
return -EINVAL;
} }
/* /*
@ -113,25 +105,18 @@ static int br_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
{ {
struct net_device *dev; struct net_device *dev;
int idx; int idx;
int s_idx = cb->args[0];
int err = 0;
read_lock(&dev_base_lock); read_lock(&dev_base_lock);
for (dev = dev_base, idx = 0; dev; dev = dev->next) { for (dev = dev_base, idx = 0; dev; dev = dev->next) {
struct net_bridge_port *p = dev->br_port;
/* not a bridge port */ /* not a bridge port */
if (!p) if (dev->br_port == NULL || idx < cb->args[0])
continue; goto skip;
if (idx < s_idx) if (br_fill_ifinfo(skb, dev->br_port, NETLINK_CB(cb->skb).pid,
goto cont; cb->nlh->nlmsg_seq, RTM_NEWLINK,
NLM_F_MULTI) < 0)
err = br_fill_ifinfo(skb, p, NETLINK_CB(cb->skb).pid,
cb->nlh->nlmsg_seq, RTM_NEWLINK, NLM_F_MULTI);
if (err <= 0)
break; break;
cont: skip:
++idx; ++idx;
} }
read_unlock(&dev_base_lock); read_unlock(&dev_base_lock);
@ -147,26 +132,27 @@ cont:
*/ */
static int br_rtm_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) static int br_rtm_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
{ {
struct rtattr **rta = arg; struct ifinfomsg *ifm;
struct ifinfomsg *ifm = NLMSG_DATA(nlh); struct nlattr *protinfo;
struct net_device *dev; struct net_device *dev;
struct net_bridge_port *p; struct net_bridge_port *p;
u8 new_state; u8 new_state;
if (nlmsg_len(nlh) < sizeof(*ifm))
return -EINVAL;
ifm = nlmsg_data(nlh);
if (ifm->ifi_family != AF_BRIDGE) if (ifm->ifi_family != AF_BRIDGE)
return -EPFNOSUPPORT; return -EPFNOSUPPORT;
/* Must pass valid state as PROTINFO */ protinfo = nlmsg_find_attr(nlh, sizeof(*ifm), IFLA_PROTINFO);
if (rta[IFLA_PROTINFO-1]) { if (!protinfo || nla_len(protinfo) < sizeof(u8))
u8 *pstate = RTA_DATA(rta[IFLA_PROTINFO-1]);
new_state = *pstate;
} else
return -EINVAL; return -EINVAL;
new_state = nla_get_u8(protinfo);
if (new_state > BR_STATE_BLOCKING) if (new_state > BR_STATE_BLOCKING)
return -EINVAL; return -EINVAL;
/* Find bridge port */
dev = __dev_get_by_index(ifm->ifi_index); dev = __dev_get_by_index(ifm->ifi_index);
if (!dev) if (!dev)
return -ENODEV; return -ENODEV;
@ -179,10 +165,8 @@ static int br_rtm_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
if (p->br->stp_enabled) if (p->br->stp_enabled)
return -EBUSY; return -EBUSY;
if (!netif_running(dev)) if (!netif_running(dev) ||
return -ENETDOWN; (!netif_carrier_ok(dev) && new_state != BR_STATE_DISABLED))
if (!netif_carrier_ok(dev) && new_state != BR_STATE_DISABLED)
return -ENETDOWN; return -ENETDOWN;
p->state = new_state; p->state = new_state;