summaryrefslogtreecommitdiff
path: root/net/bridge
diff options
context:
space:
mode:
Diffstat (limited to 'net/bridge')
-rw-r--r--net/bridge/br_ioctl.c9
-rw-r--r--net/bridge/br_netfilter.c3
-rw-r--r--net/bridge/br_netlink.c113
-rw-r--r--net/bridge/netfilter/ebt_802_3.c2
-rw-r--r--net/bridge/netfilter/ebt_among.c22
-rw-r--r--net/bridge/netfilter/ebt_arp.c6
-rw-r--r--net/bridge/netfilter/ebt_ip.c4
-rw-r--r--net/bridge/netfilter/ebt_log.c6
-rw-r--r--net/bridge/netfilter/ebt_mark.c14
-rw-r--r--net/bridge/netfilter/ebt_mark_m.c4
-rw-r--r--net/bridge/netfilter/ebt_snat.c27
-rw-r--r--net/bridge/netfilter/ebt_ulog.c2
-rw-r--r--net/bridge/netfilter/ebt_vlan.c2
-rw-r--r--net/bridge/netfilter/ebtable_broute.c2
-rw-r--r--net/bridge/netfilter/ebtable_filter.c2
-rw-r--r--net/bridge/netfilter/ebtable_nat.c2
-rw-r--r--net/bridge/netfilter/ebtables.c222
17 files changed, 250 insertions, 192 deletions
diff --git a/net/bridge/br_ioctl.c b/net/bridge/br_ioctl.c
index 4e4119a1213..4c61a7e0a86 100644
--- a/net/bridge/br_ioctl.c
+++ b/net/bridge/br_ioctl.c
@@ -58,12 +58,13 @@ static int get_fdb_entries(struct net_bridge *br, void __user *userbuf,
{
int num;
void *buf;
- size_t size = maxnum * sizeof(struct __fdb_entry);
+ size_t size;
- if (size > PAGE_SIZE) {
- size = PAGE_SIZE;
+ /* Clamp size to PAGE_SIZE, test maxnum to avoid overflow */
+ if (maxnum > PAGE_SIZE/sizeof(struct __fdb_entry))
maxnum = PAGE_SIZE/sizeof(struct __fdb_entry);
- }
+
+ size = maxnum * sizeof(struct __fdb_entry);
buf = kmalloc(size, GFP_USER);
if (!buf)
diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c
index ac181be13d8..ac47ba2ba02 100644
--- a/net/bridge/br_netfilter.c
+++ b/net/bridge/br_netfilter.c
@@ -40,7 +40,6 @@
#include <net/route.h>
#include <asm/uaccess.h>
-#include <asm/checksum.h>
#include "br_private.h"
#ifdef CONFIG_SYSCTL
#include <linux/sysctl.h>
@@ -381,7 +380,7 @@ static int check_hbh_len(struct sk_buff *skb)
case IPV6_TLV_JUMBO:
if (skb->nh.raw[off + 1] != 4 || (off & 3) != 2)
goto bad;
- pkt_len = ntohl(*(u32 *) (skb->nh.raw + off + 2));
+ pkt_len = ntohl(*(__be32 *) (skb->nh.raw + off + 2));
if (pkt_len <= IPV6_MAXPLEN ||
skb->nh.ipv6h->payload_len)
goto bad;
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index 8f661195d09..a9139682c49 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -15,6 +15,18 @@
#include <net/netlink.h>
#include "br_private.h"
+static inline size_t br_nlmsg_size(void)
+{
+ return NLMSG_ALIGN(sizeof(struct ifinfomsg))
+ + nla_total_size(IFNAMSIZ) /* IFLA_IFNAME */
+ + nla_total_size(MAX_ADDR_LEN) /* IFLA_ADDRESS */
+ + nla_total_size(4) /* IFLA_MASTER */
+ + nla_total_size(4) /* IFLA_MTU */
+ + nla_total_size(4) /* IFLA_LINK */
+ + nla_total_size(1) /* IFLA_OPERSTATE */
+ + nla_total_size(1); /* IFLA_PROTINFO */
+}
+
/*
* Create one netlink message for one interface
* Contains port and master info as well as carrier and bridge state.
@@ -24,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_device *dev = port->dev;
- struct ifinfomsg *r;
+ struct ifinfomsg *hdr;
struct nlmsghdr *nlh;
- unsigned char *b = skb->tail;
- u32 mtu = dev->mtu;
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",
event, dev->name, br->dev->name);
- nlh = NLMSG_NEW(skb, pid, seq, event, sizeof(*r), flags);
- r = NLMSG_DATA(nlh);
- r->ifi_family = AF_BRIDGE;
- 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;
+ nlh = nlmsg_put(skb, pid, seq, event, sizeof(*hdr), flags);
+ if (nlh == NULL)
+ return -ENOBUFS;
- 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)
- 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)
- RTA_PUT(skb, IFLA_LINK, sizeof(int), &dev->iflink);
-
-
- RTA_PUT(skb, IFLA_OPERSTATE, sizeof(operstate), &operstate);
+ NLA_PUT_U32(skb, IFLA_LINK, dev->iflink);
if (event == RTM_NEWLINK)
- RTA_PUT(skb, IFLA_PROTINFO, sizeof(portstate), &portstate);
-
- nlh->nlmsg_len = skb->tail - b;
-
- return skb->len;
+ NLA_PUT_U8(skb, IFLA_PROTINFO, port->state);
-nlmsg_failure:
-rtattr_failure:
+ return nlmsg_end(skb, nlh);
- skb_trim(skb, b - skb->data);
- return -EINVAL;
+nla_put_failure:
+ return nlmsg_cancel(skb, nlh);
}
/*
@@ -77,19 +81,16 @@ rtattr_failure:
void br_ifinfo_notify(int event, struct net_bridge_port *port)
{
struct sk_buff *skb;
- int payload = sizeof(struct ifinfomsg) + 128;
int err = -ENOBUFS;
pr_debug("bridge notify event=%d\n", event);
- skb = nlmsg_new(nlmsg_total_size(payload), GFP_ATOMIC);
+ skb = nlmsg_new(br_nlmsg_size(), GFP_ATOMIC);
if (skb == NULL)
goto errout;
err = br_fill_ifinfo(skb, port, 0, 0, event, 0);
- if (err < 0) {
- kfree_skb(skb);
- goto errout;
- }
+ /* failure implies BUG in br_nlmsg_size() */
+ BUG_ON(err < 0);
err = rtnl_notify(skb, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC);
errout:
@@ -104,25 +105,18 @@ static int br_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
{
struct net_device *dev;
int idx;
- int s_idx = cb->args[0];
- int err = 0;
read_lock(&dev_base_lock);
for (dev = dev_base, idx = 0; dev; dev = dev->next) {
- struct net_bridge_port *p = dev->br_port;
-
/* not a bridge port */
- if (!p)
- continue;
-
- if (idx < s_idx)
- goto cont;
+ if (dev->br_port == NULL || idx < cb->args[0])
+ goto skip;
- err = br_fill_ifinfo(skb, p, NETLINK_CB(cb->skb).pid,
- cb->nlh->nlmsg_seq, RTM_NEWLINK, NLM_F_MULTI);
- if (err <= 0)
+ if (br_fill_ifinfo(skb, dev->br_port, NETLINK_CB(cb->skb).pid,
+ cb->nlh->nlmsg_seq, RTM_NEWLINK,
+ NLM_F_MULTI) < 0)
break;
-cont:
+skip:
++idx;
}
read_unlock(&dev_base_lock);
@@ -138,26 +132,27 @@ cont:
*/
static int br_rtm_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
{
- struct rtattr **rta = arg;
- struct ifinfomsg *ifm = NLMSG_DATA(nlh);
+ struct ifinfomsg *ifm;
+ struct nlattr *protinfo;
struct net_device *dev;
struct net_bridge_port *p;
u8 new_state;
+ if (nlmsg_len(nlh) < sizeof(*ifm))
+ return -EINVAL;
+
+ ifm = nlmsg_data(nlh);
if (ifm->ifi_family != AF_BRIDGE)
return -EPFNOSUPPORT;
- /* Must pass valid state as PROTINFO */
- if (rta[IFLA_PROTINFO-1]) {
- u8 *pstate = RTA_DATA(rta[IFLA_PROTINFO-1]);
- new_state = *pstate;
- } else
+ protinfo = nlmsg_find_attr(nlh, sizeof(*ifm), IFLA_PROTINFO);
+ if (!protinfo || nla_len(protinfo) < sizeof(u8))
return -EINVAL;
+ new_state = nla_get_u8(protinfo);
if (new_state > BR_STATE_BLOCKING)
return -EINVAL;
- /* Find bridge port */
dev = __dev_get_by_index(ifm->ifi_index);
if (!dev)
return -ENODEV;
@@ -170,10 +165,8 @@ static int br_rtm_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
if (p->br->stp_enabled)
return -EBUSY;
- if (!netif_running(dev))
- return -ENETDOWN;
-
- if (!netif_carrier_ok(dev) && new_state != BR_STATE_DISABLED)
+ if (!netif_running(dev) ||
+ (!netif_carrier_ok(dev) && new_state != BR_STATE_DISABLED))
return -ENETDOWN;
p->state = new_state;
diff --git a/net/bridge/netfilter/ebt_802_3.c b/net/bridge/netfilter/ebt_802_3.c
index d42f63f5e9f..9abbc09ccdc 100644
--- a/net/bridge/netfilter/ebt_802_3.c
+++ b/net/bridge/netfilter/ebt_802_3.c
@@ -17,7 +17,7 @@ static int ebt_filter_802_3(const struct sk_buff *skb, const struct net_device *
{
struct ebt_802_3_info *info = (struct ebt_802_3_info *)data;
struct ebt_802_3_hdr *hdr = ebt_802_3_hdr(skb);
- uint16_t type = hdr->llc.ui.ctrl & IS_UI ? hdr->llc.ui.type : hdr->llc.ni.type;
+ __be16 type = hdr->llc.ui.ctrl & IS_UI ? hdr->llc.ui.type : hdr->llc.ni.type;
if (info->bitmask & EBT_802_3_SAP) {
if (FWINV(info->sap != hdr->llc.ui.ssap, EBT_802_3_SAP))
diff --git a/net/bridge/netfilter/ebt_among.c b/net/bridge/netfilter/ebt_among.c
index a614485828a..ce97c4285f9 100644
--- a/net/bridge/netfilter/ebt_among.c
+++ b/net/bridge/netfilter/ebt_among.c
@@ -15,7 +15,7 @@
#include <linux/module.h>
static int ebt_mac_wormhash_contains(const struct ebt_mac_wormhash *wh,
- const char *mac, uint32_t ip)
+ const char *mac, __be32 ip)
{
/* You may be puzzled as to how this code works.
* Some tricks were used, refer to
@@ -70,7 +70,7 @@ static int ebt_mac_wormhash_check_integrity(const struct ebt_mac_wormhash
return 0;
}
-static int get_ip_dst(const struct sk_buff *skb, uint32_t *addr)
+static int get_ip_dst(const struct sk_buff *skb, __be32 *addr)
{
if (eth_hdr(skb)->h_proto == htons(ETH_P_IP)) {
struct iphdr _iph, *ih;
@@ -81,16 +81,16 @@ static int get_ip_dst(const struct sk_buff *skb, uint32_t *addr)
*addr = ih->daddr;
} else if (eth_hdr(skb)->h_proto == htons(ETH_P_ARP)) {
struct arphdr _arph, *ah;
- uint32_t buf, *bp;
+ __be32 buf, *bp;
ah = skb_header_pointer(skb, 0, sizeof(_arph), &_arph);
if (ah == NULL ||
- ah->ar_pln != sizeof(uint32_t) ||
+ ah->ar_pln != sizeof(__be32) ||
ah->ar_hln != ETH_ALEN)
return -1;
bp = skb_header_pointer(skb, sizeof(struct arphdr) +
- 2 * ETH_ALEN + sizeof(uint32_t),
- sizeof(uint32_t), &buf);
+ 2 * ETH_ALEN + sizeof(__be32),
+ sizeof(__be32), &buf);
if (bp == NULL)
return -1;
*addr = *bp;
@@ -98,7 +98,7 @@ static int get_ip_dst(const struct sk_buff *skb, uint32_t *addr)
return 0;
}
-static int get_ip_src(const struct sk_buff *skb, uint32_t *addr)
+static int get_ip_src(const struct sk_buff *skb, __be32 *addr)
{
if (eth_hdr(skb)->h_proto == htons(ETH_P_IP)) {
struct iphdr _iph, *ih;
@@ -109,15 +109,15 @@ static int get_ip_src(const struct sk_buff *skb, uint32_t *addr)
*addr = ih->saddr;
} else if (eth_hdr(skb)->h_proto == htons(ETH_P_ARP)) {
struct arphdr _arph, *ah;
- uint32_t buf, *bp;
+ __be32 buf, *bp;
ah = skb_header_pointer(skb, 0, sizeof(_arph), &_arph);
if (ah == NULL ||
- ah->ar_pln != sizeof(uint32_t) ||
+ ah->ar_pln != sizeof(__be32) ||
ah->ar_hln != ETH_ALEN)
return -1;
bp = skb_header_pointer(skb, sizeof(struct arphdr) +
- ETH_ALEN, sizeof(uint32_t), &buf);
+ ETH_ALEN, sizeof(__be32), &buf);
if (bp == NULL)
return -1;
*addr = *bp;
@@ -133,7 +133,7 @@ static int ebt_filter_among(const struct sk_buff *skb,
struct ebt_among_info *info = (struct ebt_among_info *) data;
const char *dmac, *smac;
const struct ebt_mac_wormhash *wh_dst, *wh_src;
- uint32_t dip = 0, sip = 0;
+ __be32 dip = 0, sip = 0;
wh_dst = ebt_among_wh_dst(info);
wh_src = ebt_among_wh_src(info);
diff --git a/net/bridge/netfilter/ebt_arp.c b/net/bridge/netfilter/ebt_arp.c
index a6c81d9f73b..9c599800a90 100644
--- a/net/bridge/netfilter/ebt_arp.c
+++ b/net/bridge/netfilter/ebt_arp.c
@@ -35,10 +35,10 @@ static int ebt_filter_arp(const struct sk_buff *skb, const struct net_device *in
return EBT_NOMATCH;
if (info->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP)) {
- uint32_t _addr, *ap;
+ __be32 _addr, *ap;
/* IPv4 addresses are always 4 bytes */
- if (ah->ar_pln != sizeof(uint32_t))
+ if (ah->ar_pln != sizeof(__be32))
return EBT_NOMATCH;
if (info->bitmask & EBT_ARP_SRC_IP) {
ap = skb_header_pointer(skb, sizeof(struct arphdr) +
@@ -53,7 +53,7 @@ static int ebt_filter_arp(const struct sk_buff *skb, const struct net_device *in
if (info->bitmask & EBT_ARP_DST_IP) {
ap = skb_header_pointer(skb, sizeof(struct arphdr) +
- 2*ah->ar_hln+sizeof(uint32_t),
+ 2*ah->ar_hln+sizeof(__be32),
sizeof(_addr), &_addr);
if (ap == NULL)
return EBT_NOMATCH;
diff --git a/net/bridge/netfilter/ebt_ip.c b/net/bridge/netfilter/ebt_ip.c
index 65b665ce57b..e4c642448e1 100644
--- a/net/bridge/netfilter/ebt_ip.c
+++ b/net/bridge/netfilter/ebt_ip.c
@@ -20,8 +20,8 @@
#include <linux/module.h>
struct tcpudphdr {
- uint16_t src;
- uint16_t dst;
+ __be16 src;
+ __be16 dst;
};
static int ebt_filter_ip(const struct sk_buff *skb, const struct net_device *in,
diff --git a/net/bridge/netfilter/ebt_log.c b/net/bridge/netfilter/ebt_log.c
index 466ed3440b7..a184f879f25 100644
--- a/net/bridge/netfilter/ebt_log.c
+++ b/net/bridge/netfilter/ebt_log.c
@@ -38,8 +38,8 @@ static int ebt_log_check(const char *tablename, unsigned int hookmask,
struct tcpudphdr
{
- uint16_t src;
- uint16_t dst;
+ __be16 src;
+ __be16 dst;
};
struct arppayload
@@ -130,7 +130,7 @@ ebt_log_packet(unsigned int pf, unsigned int hooknum,
* then log the ARP payload */
if (ah->ar_hrd == htons(1) &&
ah->ar_hln == ETH_ALEN &&
- ah->ar_pln == sizeof(uint32_t)) {
+ ah->ar_pln == sizeof(__be32)) {
struct arppayload _arpp, *ap;
ap = skb_header_pointer(skb, sizeof(_arph),
diff --git a/net/bridge/netfilter/ebt_mark.c b/net/bridge/netfilter/ebt_mark.c
index b54306a934e..62d23c7b25e 100644
--- a/net/bridge/netfilter/ebt_mark.c
+++ b/net/bridge/netfilter/ebt_mark.c
@@ -25,15 +25,15 @@ static int ebt_target_mark(struct sk_buff **pskb, unsigned int hooknr,
int action = info->target & -16;
if (action == MARK_SET_VALUE)
- (*pskb)->nfmark = info->mark;
+ (*pskb)->mark = info->mark;
else if (action == MARK_OR_VALUE)
- (*pskb)->nfmark |= info->mark;
+ (*pskb)->mark |= info->mark;
else if (action == MARK_AND_VALUE)
- (*pskb)->nfmark &= info->mark;
+ (*pskb)->mark &= info->mark;
else
- (*pskb)->nfmark ^= info->mark;
+ (*pskb)->mark ^= info->mark;
- return info->target | -16;
+ return info->target | ~EBT_VERDICT_BITS;
}
static int ebt_target_mark_check(const char *tablename, unsigned int hookmask,
@@ -44,13 +44,13 @@ static int ebt_target_mark_check(const char *tablename, unsigned int hookmask,
if (datalen != EBT_ALIGN(sizeof(struct ebt_mark_t_info)))
return -EINVAL;
- tmp = info->target | -16;
+ tmp = info->target | ~EBT_VERDICT_BITS;
if (BASE_CHAIN && tmp == EBT_RETURN)
return -EINVAL;
CLEAR_BASE_CHAIN_BIT;
if (tmp < -NUM_STANDARD_TARGETS || tmp >= 0)
return -EINVAL;
- tmp = info->target & -16;
+ tmp = info->target & ~EBT_VERDICT_BITS;
if (tmp != MARK_SET_VALUE && tmp != MARK_OR_VALUE &&
tmp != MARK_AND_VALUE && tmp != MARK_XOR_VALUE)
return -EINVAL;
diff --git a/net/bridge/netfilter/ebt_mark_m.c b/net/bridge/netfilter/ebt_mark_m.c
index a6413e4b498..025869ee0b6 100644
--- a/net/bridge/netfilter/ebt_mark_m.c
+++ b/net/bridge/netfilter/ebt_mark_m.c
@@ -19,8 +19,8 @@ static int ebt_filter_mark(const struct sk_buff *skb,
struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
if (info->bitmask & EBT_MARK_OR)
- return !(!!(skb->nfmark & info->mask) ^ info->invert);
- return !(((skb->nfmark & info->mask) == info->mark) ^ info->invert);
+ return !(!!(skb->mark & info->mask) ^ info->invert);
+ return !(((skb->mark & info->mask) == info->mark) ^ info->invert);
}
static int ebt_mark_check(const char *tablename, unsigned int hookmask,
diff --git a/net/bridge/netfilter/ebt_snat.c b/net/bridge/netfilter/ebt_snat.c
index cbb33e24ca8..a50722182bf 100644
--- a/net/bridge/netfilter/ebt_snat.c
+++ b/net/bridge/netfilter/ebt_snat.c
@@ -12,6 +12,8 @@
#include <linux/netfilter_bridge/ebt_nat.h>
#include <linux/module.h>
#include <net/sock.h>
+#include <linux/if_arp.h>
+#include <net/arp.h>
static int ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
const struct net_device *in, const struct net_device *out,
@@ -31,24 +33,43 @@ static int ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
*pskb = nskb;
}
memcpy(eth_hdr(*pskb)->h_source, info->mac, ETH_ALEN);
- return info->target;
+ if (!(info->target & NAT_ARP_BIT) &&
+ eth_hdr(*pskb)->h_proto == htons(ETH_P_ARP)) {
+ struct arphdr _ah, *ap;
+
+ ap = skb_header_pointer(*pskb, 0, sizeof(_ah), &_ah);
+ if (ap == NULL)
+ return EBT_DROP;
+ if (ap->ar_hln != ETH_ALEN)
+ goto out;
+ if (skb_store_bits(*pskb, sizeof(_ah), info->mac,ETH_ALEN))
+ return EBT_DROP;
+ }
+out:
+ return info->target | ~EBT_VERDICT_BITS;
}
static int ebt_target_snat_check(const char *tablename, unsigned int hookmask,
const struct ebt_entry *e, void *data, unsigned int datalen)
{
struct ebt_nat_info *info = (struct ebt_nat_info *) data;
+ int tmp;
if (datalen != EBT_ALIGN(sizeof(struct ebt_nat_info)))
return -EINVAL;
- if (BASE_CHAIN && info->target == EBT_RETURN)
+ tmp = info->target | ~EBT_VERDICT_BITS;
+ if (BASE_CHAIN && tmp == EBT_RETURN)
return -EINVAL;
CLEAR_BASE_CHAIN_BIT;
if (strcmp(tablename, "nat"))
return -EINVAL;
if (hookmask & ~(1 << NF_BR_POST_ROUTING))
return -EINVAL;
- if (INVALID_TARGET)
+
+ if (tmp < -NUM_STANDARD_TARGETS || tmp >= 0)
+ return -EINVAL;
+ tmp = info->target | EBT_VERDICT_BITS;
+ if ((tmp & ~NAT_ARP_BIT) != ~NAT_ARP_BIT)
return -EINVAL;
return 0;
}
diff --git a/net/bridge/netfilter/ebt_ulog.c b/net/bridge/netfilter/ebt_ulog.c
index 9f950db3b76..c1af68b5a29 100644
--- a/net/bridge/netfilter/ebt_ulog.c
+++ b/net/bridge/netfilter/ebt_ulog.c
@@ -168,7 +168,7 @@ static void ebt_ulog_packet(unsigned int hooknr, const struct sk_buff *skb,
if (ub->qlen == 1)
skb_set_timestamp(ub->skb, &pm->stamp);
pm->data_len = copy_len;
- pm->mark = skb->nfmark;
+ pm->mark = skb->mark;
pm->hook = hooknr;
if (uloginfo->prefix != NULL)
strcpy(pm->prefix, uloginfo->prefix);
diff --git a/net/bridge/netfilter/ebt_vlan.c b/net/bridge/netfilter/ebt_vlan.c
index a2b452862b7..7ee37762296 100644
--- a/net/bridge/netfilter/ebt_vlan.c
+++ b/net/bridge/netfilter/ebt_vlan.c
@@ -55,7 +55,7 @@ ebt_filter_vlan(const struct sk_buff *skb,
unsigned short id; /* VLAN ID, given from frame TCI */
unsigned char prio; /* user_priority, given from frame TCI */
/* VLAN encapsulated Type/Length field, given from orig frame */
- unsigned short encap;
+ __be16 encap;
fp = skb_header_pointer(skb, 0, sizeof(_frame), &_frame);
if (fp == NULL)
diff --git a/net/bridge/netfilter/ebtable_broute.c b/net/bridge/netfilter/ebtable_broute.c
index 9a6e548e148..d37ce047893 100644
--- a/net/bridge/netfilter/ebtable_broute.c
+++ b/net/bridge/netfilter/ebtable_broute.c
@@ -23,7 +23,7 @@ static struct ebt_entries initial_chain = {
.policy = EBT_ACCEPT,
};
-static struct ebt_replace initial_table =
+static struct ebt_replace_kernel initial_table =
{
.name = "broute",
.valid_hooks = 1 << NF_BR_BROUTING,
diff --git a/net/bridge/netfilter/ebtable_filter.c b/net/bridge/netfilter/ebtable_filter.c
index 3d5bd44f239..127135ead2d 100644
--- a/net/bridge/netfilter/ebtable_filter.c
+++ b/net/bridge/netfilter/ebtable_filter.c
@@ -30,7 +30,7 @@ static struct ebt_entries initial_chains[] =
},
};
-static struct ebt_replace initial_table =
+static struct ebt_replace_kernel initial_table =
{
.name = "filter",
.valid_hooks = FILTER_VALID_HOOKS,
diff --git a/net/bridge/netfilter/ebtable_nat.c b/net/bridge/netfilter/ebtable_nat.c
index 04dd42efda1..9c50488b62e 100644
--- a/net/bridge/netfilter/ebtable_nat.c
+++ b/net/bridge/netfilter/ebtable_nat.c
@@ -30,7 +30,7 @@ static struct ebt_entries initial_chains[] =
}
};
-static struct ebt_replace initial_table =
+static struct ebt_replace_kernel initial_table =
{
.name = "nat",
.valid_hooks = NAT_VALID_HOOKS,
diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c
index 9f85666f29f..bee558a4180 100644
--- a/net/bridge/netfilter/ebtables.c
+++ b/net/bridge/netfilter/ebtables.c
@@ -338,10 +338,11 @@ ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e,
const char *name, unsigned int hookmask, unsigned int *cnt)
{
struct ebt_match *match;
+ size_t left = ((char *)e + e->watchers_offset) - (char *)m;
int ret;
- if (((char *)m) + m->match_size + sizeof(struct ebt_entry_match) >
- ((char *)e) + e->watchers_offset)
+ if (left < sizeof(struct ebt_entry_match) ||
+ left - sizeof(struct ebt_entry_match) < m->match_size)
return -EINVAL;
match = find_match_lock(m->u.name, &ret, &ebt_mutex);
if (!match)
@@ -367,10 +368,11 @@ ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e,
const char *name, unsigned int hookmask, unsigned int *cnt)
{
struct ebt_watcher *watcher;
+ size_t left = ((char *)e + e->target_offset) - (char *)w;
int ret;
- if (((char *)w) + w->watcher_size + sizeof(struct ebt_entry_watcher) >
- ((char *)e) + e->target_offset)
+ if (left < sizeof(struct ebt_entry_watcher) ||
+ left - sizeof(struct ebt_entry_watcher) < w->watcher_size)
return -EINVAL;
watcher = find_watcher_lock(w->u.name, &ret, &ebt_mutex);
if (!watcher)
@@ -391,35 +393,91 @@ ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e,
return 0;
}
+static int ebt_verify_pointers(struct ebt_replace *repl,
+ struct ebt_table_info *newinfo)
+{
+ unsigned int limit = repl->entries_size;
+ unsigned int valid_hooks = repl->valid_hooks;
+ unsigned int offset = 0;
+ int i;
+
+ for (i = 0; i < NF_BR_NUMHOOKS; i++)
+ newinfo->hook_entry[i] = NULL;
+
+ newinfo->entries_size = repl->entries_size;
+ newinfo->nentries = repl->nentries;
+
+ while (offset < limit) {
+ size_t left = limit - offset;
+ struct ebt_entry *e = (void *)newinfo->entries + offset;
+
+ if (left < sizeof(unsigned int))
+ break;
+
+ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+ if ((valid_hooks & (1 << i)) == 0)
+ continue;
+ if ((char __user *)repl->hook_entry[i] ==
+ repl->entries + offset)
+ break;
+ }
+
+ if (i != NF_BR_NUMHOOKS || !(e->bitmask & EBT_ENTRY_OR_ENTRIES)) {
+ if (e->bitmask != 0) {
+ /* we make userspace set this right,
+ so there is no misunderstanding */
+ BUGPRINT("EBT_ENTRY_OR_ENTRIES shouldn't be set "
+ "in distinguisher\n");
+ return -EINVAL;
+ }
+ if (i != NF_BR_NUMHOOKS)
+ newinfo->hook_entry[i] = (struct ebt_entries *)e;
+ if (left < sizeof(struct ebt_entries))
+ break;
+ offset += sizeof(struct ebt_entries);
+ } else {
+ if (left < sizeof(struct ebt_entry))
+ break;
+ if (left < e->next_offset)
+ break;
+ offset += e->next_offset;
+ }
+ }
+ if (offset != limit) {
+ BUGPRINT("entries_size too small\n");
+ return -EINVAL;
+ }
+
+ /* check if all valid hooks have a chain */
+ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+ if (!newinfo->hook_entry[i] &&
+ (valid_hooks & (1 << i))) {
+ BUGPRINT("Valid hook without chain\n");
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
/*
* this one is very careful, as it is the first function
* to parse the userspace data
*/
static inline int
ebt_check_entry_size_and_hooks(struct ebt_entry *e,
- struct ebt_table_info *newinfo, char *base, char *limit,
- struct ebt_entries **hook_entries, unsigned int *n, unsigned int *cnt,
- unsigned int *totalcnt, unsigned int *udc_cnt, unsigned int valid_hooks)
+ struct ebt_table_info *newinfo,
+ unsigned int *n, unsigned int *cnt,
+ unsigned int *totalcnt, unsigned int *udc_cnt)
{
int i;
for (i = 0; i < NF_BR_NUMHOOKS; i++) {
- if ((valid_hooks & (1 << i)) == 0)
- continue;
- if ( (char *)hook_entries[i] - base ==
- (char *)e - newinfo->entries)
+ if ((void *)e == (void *)newinfo->hook_entry[i])
break;
}
/* beginning of a new chain
if i == NF_BR_NUMHOOKS it must be a user defined chain */
- if (i != NF_BR_NUMHOOKS || !(e->bitmask & EBT_ENTRY_OR_ENTRIES)) {
- if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) != 0) {
- /* we make userspace set this right,
- so there is no misunderstanding */
- BUGPRINT("EBT_ENTRY_OR_ENTRIES shouldn't be set "
- "in distinguisher\n");
- return -EINVAL;
- }
+ if (i != NF_BR_NUMHOOKS || !e->bitmask) {
/* this checks if the previous chain has as many entries
as it said it has */
if (*n != *cnt) {
@@ -427,12 +485,6 @@ ebt_check_entry_size_and_hooks(struct ebt_entry *e,
"in the chain\n");
return -EINVAL;
}
- /* before we look at the struct, be sure it is not too big */
- if ((char *)hook_entries[i] + sizeof(struct ebt_entries)
- > limit) {
- BUGPRINT("entries_size too small\n");
- return -EINVAL;
- }
if (((struct ebt_entries *)e)->policy != EBT_DROP &&
((struct ebt_entries *)e)->policy != EBT_ACCEPT) {
/* only RETURN from udc */
@@ -444,8 +496,6 @@ ebt_check_entry_size_and_hooks(struct ebt_entry *e,
}
if (i == NF_BR_NUMHOOKS) /* it's a user defined chain */
(*udc_cnt)++;
- else
- newinfo->hook_entry[i] = (struct ebt_entries *)e;
if (((struct ebt_entries *)e)->counter_offset != *totalcnt) {
BUGPRINT("counter_offset != totalcnt");
return -EINVAL;
@@ -466,7 +516,6 @@ ebt_check_entry_size_and_hooks(struct ebt_entry *e,
BUGPRINT("target size too small\n");
return -EINVAL;
}
-
(*cnt)++;
(*totalcnt)++;
return 0;
@@ -485,17 +534,14 @@ struct ebt_cl_stack
*/
static inline int
ebt_get_udc_positions(struct ebt_entry *e, struct ebt_table_info *newinfo,
- struct ebt_entries **hook_entries, unsigned int *n, unsigned int valid_hooks,
- struct ebt_cl_stack *udc)
+ unsigned int *n, struct ebt_cl_stack *udc)
{
int i;
/* we're only interested in chain starts */
- if (e->bitmask & EBT_ENTRY_OR_ENTRIES)
+ if (e->bitmask)
return 0;
for (i = 0; i < NF_BR_NUMHOOKS; i++) {
- if ((valid_hooks & (1 << i)) == 0)
- continue;
if (newinfo->hook_entry[i] == (struct ebt_entries *)e)
break;
}
@@ -541,7 +587,7 @@ ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
{
struct ebt_entry_target *t;
- if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
+ if (e->bitmask == 0)
return 0;
/* we're done */
if (cnt && (*cnt)-- == 0)
@@ -558,16 +604,17 @@ ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
static inline int
ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
- const char *name, unsigned int *cnt, unsigned int valid_hooks,
+ const char *name, unsigned int *cnt,
struct ebt_cl_stack *cl_s, unsigned int udc_cnt)
{
struct ebt_entry_target *t;
struct ebt_target *target;
unsigned int i, j, hook = 0, hookmask = 0;
+ size_t gap = e->next_offset - e->target_offset;
int ret;
/* don't mess with the struct ebt_entries */
- if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
+ if (e->bitmask == 0)
return 0;
if (e->bitmask & ~EBT_F_MASK) {
@@ -584,7 +631,7 @@ ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
}
/* what hook do we belong to? */
for (i = 0; i < NF_BR_NUMHOOKS; i++) {
- if ((valid_hooks & (1 << i)) == 0)
+ if (!newinfo->hook_entry[i])
continue;
if ((char *)newinfo->hook_entry[i] < (char *)e)
hook = i;
@@ -625,8 +672,7 @@ ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
t->u.target = target;
if (t->u.target == &ebt_standard_target) {
- if (e->target_offset + sizeof(struct ebt_standard_target) >
- e->next_offset) {
+ if (gap < sizeof(struct ebt_standard_target)) {
BUGPRINT("Standard target size too big\n");
ret = -EFAULT;
goto cleanup_watchers;
@@ -637,8 +683,7 @@ ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
ret = -EFAULT;
goto cleanup_watchers;
}
- } else if ((e->target_offset + t->target_size +
- sizeof(struct ebt_entry_target) > e->next_offset) ||
+ } else if (t->target_size > gap - sizeof(struct ebt_entry_target) ||
(t->u.target->check &&
t->u.target->check(name, hookmask, e, t->data, t->target_size) != 0)){
module_put(t->u.target->me);
@@ -708,7 +753,9 @@ static int check_chainloops(struct ebt_entries *chain, struct ebt_cl_stack *cl_s
BUGPRINT("loop\n");
return -1;
}
- /* this can't be 0, so the above test is correct */
+ if (cl_s[i].hookmask & (1 << hooknr))
+ goto letscontinue;
+ /* this can't be 0, so the loop test is correct */
cl_s[i].cs.n = pos + 1;
pos = 0;
cl_s[i].cs.e = ((void *)e + e->next_offset);
@@ -728,42 +775,35 @@ letscontinue:
}
/* do the parsing of the table/chains/entries/matches/watchers/targets, heh */
-static int translate_table(struct ebt_replace *repl,
- struct ebt_table_info *newinfo)
+static int translate_table(char *name, struct ebt_table_info *newinfo)
{
unsigned int i, j, k, udc_cnt;
int ret;
struct ebt_cl_stack *cl_s = NULL; /* used in the checking for chain loops */
i = 0;
- while (i < NF_BR_NUMHOOKS && !(repl->valid_hooks & (1 << i)))
+ while (i < NF_BR_NUMHOOKS && !newinfo->hook_entry[i])
i++;
if (i == NF_BR_NUMHOOKS) {
BUGPRINT("No valid hooks specified\n");
return -EINVAL;
}
- if (repl->hook_entry[i] != (struct ebt_entries *)repl->entries) {
+ if (newinfo->hook_entry[i] != (struct ebt_entries *)newinfo->entries) {
BUGPRINT("Chains don't start at beginning\n");
return -EINVAL;
}
/* make sure chains are ordered after each other in same order
as their corresponding hooks */
for (j = i + 1; j < NF_BR_NUMHOOKS; j++) {
- if (!(repl->valid_hooks & (1 << j)))
+ if (!newinfo->hook_entry[j])
continue;
- if ( repl->hook_entry[j] <= repl->hook_entry[i] ) {
+ if (newinfo->hook_entry[j] <= newinfo->hook_entry[i]) {
BUGPRINT("Hook order must be followed\n");
return -EINVAL;
}
i = j;
}
- for (i = 0; i < NF_BR_NUMHOOKS; i++)
- newinfo->hook_entry[i] = NULL;
-
- newinfo->entries_size = repl->entries_size;
- newinfo->nentries = repl->nentries;
-
/* do some early checkings and initialize some things */
i = 0; /* holds the expected nr. of entries for the chain */
j = 0; /* holds the up to now counted entries for the chain */
@@ -771,9 +811,8 @@ static int translate_table(struct ebt_replace *repl,
newinfo->nentries afterwards */
udc_cnt = 0; /* will hold the nr. of user defined chains (udc) */
ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
- ebt_check_entry_size_and_hooks, newinfo, repl->entries,
- repl->entries + repl->entries_size, repl->hook_entry, &i, &j, &k,
- &udc_cnt, repl->valid_hooks);
+ ebt_check_entry_size_and_hooks, newinfo,
+ &i, &j, &k, &udc_cnt);
if (ret != 0)
return ret;
@@ -788,15 +827,6 @@ static int translate_table(struct ebt_replace *repl,
return -EINVAL;
}
- /* check if all valid hooks have a chain */
- for (i = 0; i < NF_BR_NUMHOOKS; i++) {
- if (newinfo->hook_entry[i] == NULL &&
- (repl->valid_hooks & (1 << i))) {
- BUGPRINT("Valid hook without chain\n");
- return -EINVAL;
- }
- }
-
/* get the location of the udc, put them in an array
while we're at it, allocate the chainstack */
if (udc_cnt) {
@@ -824,8 +854,7 @@ static int translate_table(struct ebt_replace *repl,
return -ENOMEM;
i = 0; /* the i'th udc */
EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
- ebt_get_udc_positions, newinfo, repl->hook_entry, &i,
- repl->valid_hooks, cl_s);
+ ebt_get_udc_positions, newinfo, &i, cl_s);
/* sanity check */
if (i != udc_cnt) {
BUGPRINT("i != udc_cnt\n");
@@ -836,7 +865,7 @@ static int translate_table(struct ebt_replace *repl,
/* Check for loops */
for (i = 0; i < NF_BR_NUMHOOKS; i++)
- if (repl->valid_hooks & (1 << i))
+ if (newinfo->hook_entry[i])
if (check_chainloops(newinfo->hook_entry[i],
cl_s, udc_cnt, i, newinfo->entries)) {
vfree(cl_s);
@@ -856,8 +885,7 @@ static int translate_table(struct ebt_replace *repl,
/* used to know what we need to clean up if something goes wrong */
i = 0;
ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
- ebt_check_entry, newinfo, repl->name, &i, repl->valid_hooks,
- cl_s, udc_cnt);
+ ebt_check_entry, newinfo, name, &i, cl_s, udc_cnt);
if (ret != 0) {
EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
ebt_cleanup_entry, &i);
@@ -954,7 +982,11 @@ static int do_replace(void __user *user, unsigned int len)
/* this can get initialized by translate_table() */
newinfo->chainstack = NULL;
- ret = translate_table(&tmp, newinfo);
+ ret = ebt_verify_pointers(&tmp, newinfo);
+ if (ret != 0)
+ goto free_counterstmp;
+
+ ret = translate_table(tmp.name, newinfo);
if (ret != 0)
goto free_counterstmp;
@@ -1125,35 +1157,47 @@ int ebt_register_table(struct ebt_table *table)
{
struct ebt_table_info *newinfo;
struct ebt_table *t;
+ struct ebt_replace_kernel *repl;
int ret, i, countersize;
+ void *p;
- if (!table || !table->table ||!table->table->entries ||
- table->table->entries_size == 0 ||
- table->table->counters || table->private) {
+ if (!table || !(repl = table->table) || !repl->entries ||
+ repl->entries_size == 0 ||
+ repl->counters || table->private) {
BUGPRINT("Bad table data for ebt_register_table!!!\n");
return -EINVAL;
}
- countersize = COUNTER_OFFSET(table->table->nentries) *
+ countersize = COUNTER_OFFSET(repl->nentries) *
(highest_possible_processor_id()+1);
newinfo = vmalloc(sizeof(*newinfo) + countersize);
ret = -ENOMEM;
if (!newinfo)
return -ENOMEM;
- newinfo->entries = vmalloc(table->table->entries_size);
- if (!(newinfo->entries))
+ p = vmalloc(repl->entries_size);
+ if (!p)
goto free_newinfo;
- memcpy(newinfo->entries, table->table->entries,
- table->table->entries_size);
+ memcpy(p, repl->entries, repl->entries_size);
+ newinfo->entries = p;
+
+ newinfo->entries_size = repl->entries_size;
+ newinfo->nentries = repl->nentries;
if (countersize)
memset(newinfo->counters, 0, countersize);
/* fill in newinfo and parse the entries */
newinfo->chainstack = NULL;
- ret = translate_table(table->table, newinfo);
+ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+ if ((repl->valid_hooks & (1 << i)) == 0)
+ newinfo->hook_entry[i] = NULL;
+ else
+ newinfo->hook_entry[i] = p +
+ ((char *)repl->hook_entry[i] - repl->entries);
+ }
+ ret = translate_table(repl->name, newinfo);
if (ret != 0) {
BUGPRINT("Translate_table failed\n");
goto free_chainstack;
@@ -1277,33 +1321,33 @@ free_tmp:
}
static inline int ebt_make_matchname(struct ebt_entry_match *m,
- char *base, char *ubase)
+ char *base, char __user *ubase)
{
- char *hlp = ubase - base + (char *)m;
+ char __user *hlp = ubase + ((char *)m - base);
if (copy_to_user(hlp, m->u.match->name, EBT_FUNCTION_MAXNAMELEN))
return -EFAULT;
return 0;
}
static inline int ebt_make_watchername(struct ebt_entry_watcher *w,
- char *base, char *ubase)
+ char *base, char __user *ubase)
{
- char *hlp = ubase - base + (char *)w;
+ char __user *hlp = ubase + ((char *)w - base);
if (copy_to_user(hlp , w->u.watcher->name, EBT_FUNCTION_MAXNAMELEN))
return -EFAULT;
return 0;
}
-static inline int ebt_make_names(struct ebt_entry *e, char *base, char *ubase)
+static inline int ebt_make_names(struct ebt_entry *e, char *base, char __user *ubase)
{
int ret;
- char *hlp;
+ char __user *hlp;
struct ebt_entry_target *t;
- if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
+ if (e->bitmask == 0)
return 0;
- hlp = ubase - base + (char *)e + e->target_offset;
+ hlp = ubase + (((char *)e + e->target_offset) - base);
t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
ret = EBT_MATCH_ITERATE(e, ebt_make_matchname, base, ubase);