Merge branch 'linus' into cpumask-for-linus
Conflicts: arch/x86/kernel/cpu/common.c
This commit is contained in:
+8
-7
@@ -29,7 +29,7 @@ static struct llc_sap *snap_sap;
|
||||
/*
|
||||
* Find a snap client by matching the 5 bytes.
|
||||
*/
|
||||
static struct datalink_proto *find_snap_client(unsigned char *desc)
|
||||
static struct datalink_proto *find_snap_client(const unsigned char *desc)
|
||||
{
|
||||
struct datalink_proto *proto = NULL, *p;
|
||||
|
||||
@@ -51,7 +51,7 @@ static int snap_rcv(struct sk_buff *skb, struct net_device *dev,
|
||||
int rc = 1;
|
||||
struct datalink_proto *proto;
|
||||
static struct packet_type snap_packet_type = {
|
||||
.type = __constant_htons(ETH_P_SNAP),
|
||||
.type = cpu_to_be16(ETH_P_SNAP),
|
||||
};
|
||||
|
||||
if (unlikely(!pskb_may_pull(skb, 5)))
|
||||
@@ -95,15 +95,16 @@ static int snap_request(struct datalink_proto *dl,
|
||||
EXPORT_SYMBOL(register_snap_client);
|
||||
EXPORT_SYMBOL(unregister_snap_client);
|
||||
|
||||
static char snap_err_msg[] __initdata =
|
||||
static const char snap_err_msg[] __initconst =
|
||||
KERN_CRIT "SNAP - unable to register with 802.2\n";
|
||||
|
||||
static int __init snap_init(void)
|
||||
{
|
||||
snap_sap = llc_sap_open(0xAA, snap_rcv);
|
||||
|
||||
if (!snap_sap)
|
||||
if (!snap_sap) {
|
||||
printk(snap_err_msg);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -121,7 +122,7 @@ module_exit(snap_exit);
|
||||
/*
|
||||
* Register SNAP clients. We don't yet use this for IP.
|
||||
*/
|
||||
struct datalink_proto *register_snap_client(unsigned char *desc,
|
||||
struct datalink_proto *register_snap_client(const unsigned char *desc,
|
||||
int (*rcvfunc)(struct sk_buff *,
|
||||
struct net_device *,
|
||||
struct packet_type *,
|
||||
@@ -136,7 +137,7 @@ struct datalink_proto *register_snap_client(unsigned char *desc,
|
||||
|
||||
proto = kmalloc(sizeof(*proto), GFP_ATOMIC);
|
||||
if (proto) {
|
||||
memcpy(proto->type, desc,5);
|
||||
memcpy(proto->type, desc, 5);
|
||||
proto->rcvfunc = rcvfunc;
|
||||
proto->header_length = 5 + 3; /* snap + 802.2 */
|
||||
proto->request = snap_request;
|
||||
|
||||
@@ -486,6 +486,7 @@ static struct rif_cache *rif_get_idx(loff_t pos)
|
||||
}
|
||||
|
||||
static void *rif_seq_start(struct seq_file *seq, loff_t *pos)
|
||||
__acquires(&rif_lock)
|
||||
{
|
||||
spin_lock_irq(&rif_lock);
|
||||
|
||||
@@ -517,6 +518,7 @@ static void *rif_seq_next(struct seq_file *seq, void *v, loff_t *pos)
|
||||
}
|
||||
|
||||
static void rif_seq_stop(struct seq_file *seq, void *v)
|
||||
__releases(&rif_lock)
|
||||
{
|
||||
spin_unlock_irq(&rif_lock);
|
||||
}
|
||||
|
||||
+2
-2
@@ -51,8 +51,8 @@ const char vlan_version[] = DRV_VERSION;
|
||||
static const char vlan_copyright[] = "Ben Greear <greearb@candelatech.com>";
|
||||
static const char vlan_buggyright[] = "David S. Miller <davem@redhat.com>";
|
||||
|
||||
static struct packet_type vlan_packet_type = {
|
||||
.type = __constant_htons(ETH_P_8021Q),
|
||||
static struct packet_type vlan_packet_type __read_mostly = {
|
||||
.type = cpu_to_be16(ETH_P_8021Q),
|
||||
.func = vlan_skb_recv, /* VLAN receive method */
|
||||
};
|
||||
|
||||
|
||||
+13
-40
@@ -89,38 +89,27 @@ static int vlan_gro_common(struct napi_struct *napi, struct vlan_group *grp,
|
||||
goto drop;
|
||||
|
||||
for (p = napi->gro_list; p; p = p->next) {
|
||||
NAPI_GRO_CB(p)->same_flow = p->dev == skb->dev;
|
||||
NAPI_GRO_CB(p)->same_flow =
|
||||
p->dev == skb->dev && !compare_ether_header(
|
||||
skb_mac_header(p), skb_gro_mac_header(skb));
|
||||
NAPI_GRO_CB(p)->flush = 0;
|
||||
}
|
||||
|
||||
return dev_gro_receive(napi, skb);
|
||||
|
||||
drop:
|
||||
return 2;
|
||||
return GRO_DROP;
|
||||
}
|
||||
|
||||
int vlan_gro_receive(struct napi_struct *napi, struct vlan_group *grp,
|
||||
unsigned int vlan_tci, struct sk_buff *skb)
|
||||
{
|
||||
int err = NET_RX_SUCCESS;
|
||||
if (netpoll_rx_on(skb))
|
||||
return vlan_hwaccel_receive_skb(skb, grp, vlan_tci);
|
||||
|
||||
if (netpoll_receive_skb(skb))
|
||||
return NET_RX_DROP;
|
||||
skb_gro_reset_offset(skb);
|
||||
|
||||
switch (vlan_gro_common(napi, grp, vlan_tci, skb)) {
|
||||
case -1:
|
||||
return netif_receive_skb(skb);
|
||||
|
||||
case 2:
|
||||
err = NET_RX_DROP;
|
||||
/* fall through */
|
||||
|
||||
case 1:
|
||||
kfree_skb(skb);
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
return napi_skb_finish(vlan_gro_common(napi, grp, vlan_tci, skb), skb);
|
||||
}
|
||||
EXPORT_SYMBOL(vlan_gro_receive);
|
||||
|
||||
@@ -128,30 +117,14 @@ int vlan_gro_frags(struct napi_struct *napi, struct vlan_group *grp,
|
||||
unsigned int vlan_tci, struct napi_gro_fraginfo *info)
|
||||
{
|
||||
struct sk_buff *skb = napi_fraginfo_skb(napi, info);
|
||||
int err = NET_RX_DROP;
|
||||
|
||||
if (!skb)
|
||||
goto out;
|
||||
return NET_RX_DROP;
|
||||
|
||||
if (netpoll_receive_skb(skb))
|
||||
goto out;
|
||||
if (netpoll_rx_on(skb))
|
||||
return vlan_hwaccel_receive_skb(skb, grp, vlan_tci);
|
||||
|
||||
err = NET_RX_SUCCESS;
|
||||
|
||||
switch (vlan_gro_common(napi, grp, vlan_tci, skb)) {
|
||||
case -1:
|
||||
return netif_receive_skb(skb);
|
||||
|
||||
case 2:
|
||||
err = NET_RX_DROP;
|
||||
/* fall through */
|
||||
|
||||
case 1:
|
||||
napi_reuse_skb(napi, skb);
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
return err;
|
||||
return napi_frags_finish(napi, skb,
|
||||
vlan_gro_common(napi, grp, vlan_tci, skb));
|
||||
}
|
||||
EXPORT_SYMBOL(vlan_gro_frags);
|
||||
|
||||
+1
-1
@@ -417,7 +417,7 @@ static int p9_fd_write(struct p9_client *client, void *v, int len)
|
||||
oldfs = get_fs();
|
||||
set_fs(get_ds());
|
||||
/* The cast to a user pointer is valid due to the set_fs() */
|
||||
ret = vfs_write(ts->wr, (void __user *)v, len, &ts->wr->f_pos);
|
||||
ret = vfs_write(ts->wr, (__force void __user *)v, len, &ts->wr->f_pos);
|
||||
set_fs(oldfs);
|
||||
|
||||
if (ret <= 0 && ret != -ERESTARTSYS && ret != -EAGAIN)
|
||||
|
||||
+14
-4
@@ -4,6 +4,7 @@
|
||||
|
||||
menuconfig NET
|
||||
bool "Networking support"
|
||||
select NLATTR
|
||||
---help---
|
||||
Unless you really know what you are doing, you should say Y here.
|
||||
The reason is that some programs need kernel networking support even
|
||||
@@ -24,9 +25,6 @@ if NET
|
||||
|
||||
menu "Networking options"
|
||||
|
||||
config COMPAT_NET_DEV_OPS
|
||||
def_bool y
|
||||
|
||||
source "net/packet/Kconfig"
|
||||
source "net/unix/Kconfig"
|
||||
source "net/xfrm/Kconfig"
|
||||
@@ -171,6 +169,7 @@ endif
|
||||
|
||||
source "net/dccp/Kconfig"
|
||||
source "net/sctp/Kconfig"
|
||||
source "net/rds/Kconfig"
|
||||
source "net/tipc/Kconfig"
|
||||
source "net/atm/Kconfig"
|
||||
source "net/802/Kconfig"
|
||||
@@ -185,6 +184,7 @@ source "net/x25/Kconfig"
|
||||
source "net/lapb/Kconfig"
|
||||
source "net/econet/Kconfig"
|
||||
source "net/wanrouter/Kconfig"
|
||||
source "net/phonet/Kconfig"
|
||||
source "net/sched/Kconfig"
|
||||
source "net/dcb/Kconfig"
|
||||
|
||||
@@ -220,6 +220,17 @@ config NET_TCPPROBE
|
||||
To compile this code as a module, choose M here: the
|
||||
module will be called tcp_probe.
|
||||
|
||||
config NET_DROP_MONITOR
|
||||
boolean "Network packet drop alerting service"
|
||||
depends on INET && EXPERIMENTAL && TRACEPOINTS
|
||||
---help---
|
||||
This feature provides an alerting service to userspace in the
|
||||
event that packets are discarded in the network stack. Alerts
|
||||
are broadcast via netlink socket to any listening user space
|
||||
process. If you don't need network drop alerts, or if you are ok
|
||||
just checking the various proc files and other utilities for
|
||||
drop statistics, say N here.
|
||||
|
||||
endmenu
|
||||
|
||||
endmenu
|
||||
@@ -229,7 +240,6 @@ source "net/can/Kconfig"
|
||||
source "net/irda/Kconfig"
|
||||
source "net/bluetooth/Kconfig"
|
||||
source "net/rxrpc/Kconfig"
|
||||
source "net/phonet/Kconfig"
|
||||
|
||||
config FIB_RULES
|
||||
bool
|
||||
|
||||
@@ -49,6 +49,7 @@ obj-y += 8021q/
|
||||
endif
|
||||
obj-$(CONFIG_IP_DCCP) += dccp/
|
||||
obj-$(CONFIG_IP_SCTP) += sctp/
|
||||
obj-$(CONFIG_RDS) += rds/
|
||||
obj-y += wireless/
|
||||
obj-$(CONFIG_MAC80211) += mac80211/
|
||||
obj-$(CONFIG_TIPC) += tipc/
|
||||
|
||||
+6
-10
@@ -1571,14 +1571,10 @@ static int atalk_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr
|
||||
usat->sat_family != AF_APPLETALK)
|
||||
return -EINVAL;
|
||||
|
||||
/* netatalk doesn't implement this check */
|
||||
/* netatalk didn't implement this check */
|
||||
if (usat->sat_addr.s_node == ATADDR_BCAST &&
|
||||
!sock_flag(sk, SOCK_BROADCAST)) {
|
||||
printk(KERN_INFO "SO_BROADCAST: Fix your netatalk as "
|
||||
"it will break before 2.2\n");
|
||||
#if 0
|
||||
return -EPERM;
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
if (sk->sk_state != TCP_ESTABLISHED)
|
||||
@@ -1860,13 +1856,13 @@ static struct notifier_block ddp_notifier = {
|
||||
.notifier_call = ddp_device_event,
|
||||
};
|
||||
|
||||
static struct packet_type ltalk_packet_type = {
|
||||
.type = __constant_htons(ETH_P_LOCALTALK),
|
||||
static struct packet_type ltalk_packet_type __read_mostly = {
|
||||
.type = cpu_to_be16(ETH_P_LOCALTALK),
|
||||
.func = ltalk_rcv,
|
||||
};
|
||||
|
||||
static struct packet_type ppptalk_packet_type = {
|
||||
.type = __constant_htons(ETH_P_PPPTALK),
|
||||
static struct packet_type ppptalk_packet_type __read_mostly = {
|
||||
.type = cpu_to_be16(ETH_P_PPPTALK),
|
||||
.func = atalk_rcv,
|
||||
};
|
||||
|
||||
@@ -1877,7 +1873,7 @@ EXPORT_SYMBOL(aarp_send_ddp);
|
||||
EXPORT_SYMBOL(atrtr_get_dev);
|
||||
EXPORT_SYMBOL(atalk_find_dev_addr);
|
||||
|
||||
static char atalk_err_snap[] __initdata =
|
||||
static const char atalk_err_snap[] __initconst =
|
||||
KERN_CRIT "Unable to register DDP with SNAP.\n";
|
||||
|
||||
/* Called by proto.c on kernel start up */
|
||||
|
||||
+4
-6
@@ -9,22 +9,20 @@
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/if_ltalk.h>
|
||||
|
||||
#ifdef CONFIG_COMPAT_NET_DEV_OPS
|
||||
static int ltalk_change_mtu(struct net_device *dev, int mtu)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ltalk_mac_addr(struct net_device *dev, void *addr)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void ltalk_setup(struct net_device *dev)
|
||||
{
|
||||
/* Fill in the fields of the device structure with localtalk-generic values. */
|
||||
|
||||
#ifdef CONFIG_COMPAT_NET_DEV_OPS
|
||||
dev->change_mtu = ltalk_change_mtu;
|
||||
dev->set_mac_address = ltalk_mac_addr;
|
||||
#endif
|
||||
|
||||
dev->type = ARPHRD_LOCALTLK;
|
||||
dev->hard_header_len = LTALK_HLEN;
|
||||
|
||||
+26
-32
@@ -83,7 +83,6 @@ struct br2684_dev {
|
||||
struct list_head br2684_devs;
|
||||
int number;
|
||||
struct list_head brvccs; /* one device <=> one vcc (before xmas) */
|
||||
struct net_device_stats stats;
|
||||
int mac_was_set;
|
||||
enum br2684_payload payload;
|
||||
};
|
||||
@@ -148,9 +147,10 @@ static struct net_device *br2684_find_dev(const struct br2684_if_spec *s)
|
||||
* the way for multiple vcc's per itf. Returns true if we can send,
|
||||
* otherwise false
|
||||
*/
|
||||
static int br2684_xmit_vcc(struct sk_buff *skb, struct br2684_dev *brdev,
|
||||
static int br2684_xmit_vcc(struct sk_buff *skb, struct net_device *dev,
|
||||
struct br2684_vcc *brvcc)
|
||||
{
|
||||
struct br2684_dev *brdev = BRPRIV(dev);
|
||||
struct atm_vcc *atmvcc;
|
||||
int minheadroom = (brvcc->encaps == e_llc) ? 10 : 2;
|
||||
|
||||
@@ -211,8 +211,8 @@ static int br2684_xmit_vcc(struct sk_buff *skb, struct br2684_dev *brdev,
|
||||
}
|
||||
atomic_add(skb->truesize, &sk_atm(atmvcc)->sk_wmem_alloc);
|
||||
ATM_SKB(skb)->atm_options = atmvcc->atm_options;
|
||||
brdev->stats.tx_packets++;
|
||||
brdev->stats.tx_bytes += skb->len;
|
||||
dev->stats.tx_packets++;
|
||||
dev->stats.tx_bytes += skb->len;
|
||||
atmvcc->send(atmvcc, skb);
|
||||
return 1;
|
||||
}
|
||||
@@ -233,14 +233,14 @@ static int br2684_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
brvcc = pick_outgoing_vcc(skb, brdev);
|
||||
if (brvcc == NULL) {
|
||||
pr_debug("no vcc attached to dev %s\n", dev->name);
|
||||
brdev->stats.tx_errors++;
|
||||
brdev->stats.tx_carrier_errors++;
|
||||
dev->stats.tx_errors++;
|
||||
dev->stats.tx_carrier_errors++;
|
||||
/* netif_stop_queue(dev); */
|
||||
dev_kfree_skb(skb);
|
||||
read_unlock(&devs_lock);
|
||||
return 0;
|
||||
}
|
||||
if (!br2684_xmit_vcc(skb, brdev, brvcc)) {
|
||||
if (!br2684_xmit_vcc(skb, dev, brvcc)) {
|
||||
/*
|
||||
* We should probably use netif_*_queue() here, but that
|
||||
* involves added complication. We need to walk before
|
||||
@@ -248,27 +248,20 @@ static int br2684_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
*
|
||||
* Don't free here! this pointer might be no longer valid!
|
||||
*/
|
||||
brdev->stats.tx_errors++;
|
||||
brdev->stats.tx_fifo_errors++;
|
||||
dev->stats.tx_errors++;
|
||||
dev->stats.tx_fifo_errors++;
|
||||
}
|
||||
read_unlock(&devs_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct net_device_stats *br2684_get_stats(struct net_device *dev)
|
||||
{
|
||||
pr_debug("br2684_get_stats\n");
|
||||
return &BRPRIV(dev)->stats;
|
||||
}
|
||||
|
||||
/*
|
||||
* We remember when the MAC gets set, so we don't override it later with
|
||||
* the ESI of the ATM card of the first VC
|
||||
*/
|
||||
static int (*my_eth_mac_addr) (struct net_device *, void *);
|
||||
static int br2684_mac_addr(struct net_device *dev, void *p)
|
||||
{
|
||||
int err = my_eth_mac_addr(dev, p);
|
||||
int err = eth_mac_addr(dev, p);
|
||||
if (!err)
|
||||
BRPRIV(dev)->mac_was_set = 1;
|
||||
return err;
|
||||
@@ -430,17 +423,17 @@ static void br2684_push(struct atm_vcc *atmvcc, struct sk_buff *skb)
|
||||
/* sigh, interface is down? */
|
||||
if (unlikely(!(net_dev->flags & IFF_UP)))
|
||||
goto dropped;
|
||||
brdev->stats.rx_packets++;
|
||||
brdev->stats.rx_bytes += skb->len;
|
||||
net_dev->stats.rx_packets++;
|
||||
net_dev->stats.rx_bytes += skb->len;
|
||||
memset(ATM_SKB(skb), 0, sizeof(struct atm_skb_data));
|
||||
netif_rx(skb);
|
||||
return;
|
||||
|
||||
dropped:
|
||||
brdev->stats.rx_dropped++;
|
||||
net_dev->stats.rx_dropped++;
|
||||
goto free_skb;
|
||||
error:
|
||||
brdev->stats.rx_errors++;
|
||||
net_dev->stats.rx_errors++;
|
||||
free_skb:
|
||||
dev_kfree_skb(skb);
|
||||
return;
|
||||
@@ -531,8 +524,8 @@ static int br2684_regvcc(struct atm_vcc *atmvcc, void __user * arg)
|
||||
|
||||
skb->next = skb->prev = NULL;
|
||||
br2684_push(atmvcc, skb);
|
||||
BRPRIV(skb->dev)->stats.rx_bytes -= skb->len;
|
||||
BRPRIV(skb->dev)->stats.rx_packets--;
|
||||
skb->dev->stats.rx_bytes -= skb->len;
|
||||
skb->dev->stats.rx_packets--;
|
||||
|
||||
skb = next;
|
||||
}
|
||||
@@ -544,17 +537,20 @@ static int br2684_regvcc(struct atm_vcc *atmvcc, void __user * arg)
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct net_device_ops br2684_netdev_ops = {
|
||||
.ndo_start_xmit = br2684_start_xmit,
|
||||
.ndo_set_mac_address = br2684_mac_addr,
|
||||
.ndo_change_mtu = eth_change_mtu,
|
||||
.ndo_validate_addr = eth_validate_addr,
|
||||
};
|
||||
|
||||
static void br2684_setup(struct net_device *netdev)
|
||||
{
|
||||
struct br2684_dev *brdev = BRPRIV(netdev);
|
||||
|
||||
ether_setup(netdev);
|
||||
brdev->net_dev = netdev;
|
||||
|
||||
my_eth_mac_addr = netdev->set_mac_address;
|
||||
netdev->set_mac_address = br2684_mac_addr;
|
||||
netdev->hard_start_xmit = br2684_start_xmit;
|
||||
netdev->get_stats = br2684_get_stats;
|
||||
netdev->netdev_ops = &br2684_netdev_ops;
|
||||
|
||||
INIT_LIST_HEAD(&brdev->brvccs);
|
||||
}
|
||||
@@ -565,10 +561,8 @@ static void br2684_setup_routed(struct net_device *netdev)
|
||||
brdev->net_dev = netdev;
|
||||
|
||||
netdev->hard_header_len = 0;
|
||||
my_eth_mac_addr = netdev->set_mac_address;
|
||||
netdev->set_mac_address = br2684_mac_addr;
|
||||
netdev->hard_start_xmit = br2684_start_xmit;
|
||||
netdev->get_stats = br2684_get_stats;
|
||||
|
||||
netdev->netdev_ops = &br2684_netdev_ops;
|
||||
netdev->addr_len = 0;
|
||||
netdev->mtu = 1500;
|
||||
netdev->type = ARPHRD_PPP;
|
||||
|
||||
+18
-21
@@ -214,15 +214,15 @@ static void clip_push(struct atm_vcc *vcc, struct sk_buff *skb)
|
||||
skb->protocol = ((__be16 *) skb->data)[3];
|
||||
skb_pull(skb, RFC1483LLC_LEN);
|
||||
if (skb->protocol == htons(ETH_P_ARP)) {
|
||||
PRIV(skb->dev)->stats.rx_packets++;
|
||||
PRIV(skb->dev)->stats.rx_bytes += skb->len;
|
||||
skb->dev->stats.rx_packets++;
|
||||
skb->dev->stats.rx_bytes += skb->len;
|
||||
clip_arp_rcv(skb);
|
||||
return;
|
||||
}
|
||||
}
|
||||
clip_vcc->last_use = jiffies;
|
||||
PRIV(skb->dev)->stats.rx_packets++;
|
||||
PRIV(skb->dev)->stats.rx_bytes += skb->len;
|
||||
skb->dev->stats.rx_packets++;
|
||||
skb->dev->stats.rx_bytes += skb->len;
|
||||
memset(ATM_SKB(skb), 0, sizeof(struct atm_skb_data));
|
||||
netif_rx(skb);
|
||||
}
|
||||
@@ -372,7 +372,7 @@ static int clip_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
if (!skb->dst) {
|
||||
printk(KERN_ERR "clip_start_xmit: skb->dst == NULL\n");
|
||||
dev_kfree_skb(skb);
|
||||
clip_priv->stats.tx_dropped++;
|
||||
dev->stats.tx_dropped++;
|
||||
return 0;
|
||||
}
|
||||
if (!skb->dst->neighbour) {
|
||||
@@ -380,13 +380,13 @@ static int clip_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
skb->dst->neighbour = clip_find_neighbour(skb->dst, 1);
|
||||
if (!skb->dst->neighbour) {
|
||||
dev_kfree_skb(skb); /* lost that one */
|
||||
clip_priv->stats.tx_dropped++;
|
||||
dev->stats.tx_dropped++;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
printk(KERN_ERR "clip_start_xmit: NO NEIGHBOUR !\n");
|
||||
dev_kfree_skb(skb);
|
||||
clip_priv->stats.tx_dropped++;
|
||||
dev->stats.tx_dropped++;
|
||||
return 0;
|
||||
}
|
||||
entry = NEIGH2ENTRY(skb->dst->neighbour);
|
||||
@@ -400,7 +400,7 @@ static int clip_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
skb_queue_tail(&entry->neigh->arp_queue, skb);
|
||||
else {
|
||||
dev_kfree_skb(skb);
|
||||
clip_priv->stats.tx_dropped++;
|
||||
dev->stats.tx_dropped++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -423,8 +423,8 @@ static int clip_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
printk(KERN_WARNING "clip_start_xmit: XOFF->XOFF transition\n");
|
||||
return 0;
|
||||
}
|
||||
clip_priv->stats.tx_packets++;
|
||||
clip_priv->stats.tx_bytes += skb->len;
|
||||
dev->stats.tx_packets++;
|
||||
dev->stats.tx_bytes += skb->len;
|
||||
vcc->send(vcc, skb);
|
||||
if (atm_may_send(vcc, 0)) {
|
||||
entry->vccs->xoff = 0;
|
||||
@@ -443,11 +443,6 @@ static int clip_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct net_device_stats *clip_get_stats(struct net_device *dev)
|
||||
{
|
||||
return &PRIV(dev)->stats;
|
||||
}
|
||||
|
||||
static int clip_mkip(struct atm_vcc *vcc, int timeout)
|
||||
{
|
||||
struct clip_vcc *clip_vcc;
|
||||
@@ -501,8 +496,8 @@ static int clip_mkip(struct atm_vcc *vcc, int timeout)
|
||||
|
||||
skb_get(skb);
|
||||
clip_push(vcc, skb);
|
||||
PRIV(skb->dev)->stats.rx_packets--;
|
||||
PRIV(skb->dev)->stats.rx_bytes -= len;
|
||||
skb->dev->stats.rx_packets--;
|
||||
skb->dev->stats.rx_bytes -= len;
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
@@ -557,11 +552,13 @@ static int clip_setentry(struct atm_vcc *vcc, __be32 ip)
|
||||
return error;
|
||||
}
|
||||
|
||||
static const struct net_device_ops clip_netdev_ops = {
|
||||
.ndo_start_xmit = clip_start_xmit,
|
||||
};
|
||||
|
||||
static void clip_setup(struct net_device *dev)
|
||||
{
|
||||
dev->hard_start_xmit = clip_start_xmit;
|
||||
/* sg_xmit ... */
|
||||
dev->get_stats = clip_get_stats;
|
||||
dev->netdev_ops = &clip_netdev_ops;
|
||||
dev->type = ARPHRD_ATM;
|
||||
dev->hard_header_len = RFC1483LLC_LEN;
|
||||
dev->mtu = RFC1626_MTU;
|
||||
@@ -621,7 +618,7 @@ static int clip_device_event(struct notifier_block *this, unsigned long event,
|
||||
}
|
||||
|
||||
/* ignore non-CLIP devices */
|
||||
if (dev->type != ARPHRD_ATM || dev->hard_start_xmit != clip_start_xmit)
|
||||
if (dev->type != ARPHRD_ATM || dev->netdev_ops != &clip_netdev_ops)
|
||||
return NOTIFY_DONE;
|
||||
|
||||
switch (event) {
|
||||
|
||||
+29
-37
@@ -62,7 +62,6 @@ static unsigned char bridge_ula_lec[] = { 0x01, 0x80, 0xc2, 0x00, 0x00 };
|
||||
static int lec_open(struct net_device *dev);
|
||||
static int lec_start_xmit(struct sk_buff *skb, struct net_device *dev);
|
||||
static int lec_close(struct net_device *dev);
|
||||
static struct net_device_stats *lec_get_stats(struct net_device *dev);
|
||||
static void lec_init(struct net_device *dev);
|
||||
static struct lec_arp_table *lec_arp_find(struct lec_priv *priv,
|
||||
const unsigned char *mac_addr);
|
||||
@@ -218,28 +217,28 @@ static unsigned char *get_tr_dst(unsigned char *packet, unsigned char *rdesc)
|
||||
|
||||
static int lec_open(struct net_device *dev)
|
||||
{
|
||||
struct lec_priv *priv = netdev_priv(dev);
|
||||
|
||||
netif_start_queue(dev);
|
||||
memset(&priv->stats, 0, sizeof(struct net_device_stats));
|
||||
memset(&dev->stats, 0, sizeof(struct net_device_stats));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __inline__ void
|
||||
lec_send(struct atm_vcc *vcc, struct sk_buff *skb, struct lec_priv *priv)
|
||||
static void
|
||||
lec_send(struct atm_vcc *vcc, struct sk_buff *skb)
|
||||
{
|
||||
struct net_device *dev = skb->dev;
|
||||
|
||||
ATM_SKB(skb)->vcc = vcc;
|
||||
ATM_SKB(skb)->atm_options = vcc->atm_options;
|
||||
|
||||
atomic_add(skb->truesize, &sk_atm(vcc)->sk_wmem_alloc);
|
||||
if (vcc->send(vcc, skb) < 0) {
|
||||
priv->stats.tx_dropped++;
|
||||
dev->stats.tx_dropped++;
|
||||
return;
|
||||
}
|
||||
|
||||
priv->stats.tx_packets++;
|
||||
priv->stats.tx_bytes += skb->len;
|
||||
dev->stats.tx_packets++;
|
||||
dev->stats.tx_bytes += skb->len;
|
||||
}
|
||||
|
||||
static void lec_tx_timeout(struct net_device *dev)
|
||||
@@ -270,7 +269,7 @@ static int lec_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
pr_debug("lec_start_xmit called\n");
|
||||
if (!priv->lecd) {
|
||||
printk("%s:No lecd attached\n", dev->name);
|
||||
priv->stats.tx_errors++;
|
||||
dev->stats.tx_errors++;
|
||||
netif_stop_queue(dev);
|
||||
return -EUNATCH;
|
||||
}
|
||||
@@ -345,7 +344,7 @@ static int lec_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
GFP_ATOMIC);
|
||||
dev_kfree_skb(skb);
|
||||
if (skb2 == NULL) {
|
||||
priv->stats.tx_dropped++;
|
||||
dev->stats.tx_dropped++;
|
||||
return 0;
|
||||
}
|
||||
skb = skb2;
|
||||
@@ -380,7 +379,7 @@ static int lec_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
("%s:lec_start_xmit: tx queue full or no arp entry, dropping, ",
|
||||
dev->name);
|
||||
pr_debug("MAC address %pM\n", lec_h->h_dest);
|
||||
priv->stats.tx_dropped++;
|
||||
dev->stats.tx_dropped++;
|
||||
dev_kfree_skb(skb);
|
||||
}
|
||||
goto out;
|
||||
@@ -392,10 +391,10 @@ static int lec_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
while (entry && (skb2 = skb_dequeue(&entry->tx_wait))) {
|
||||
pr_debug("lec.c: emptying tx queue, ");
|
||||
pr_debug("MAC address %pM\n", lec_h->h_dest);
|
||||
lec_send(vcc, skb2, priv);
|
||||
lec_send(vcc, skb2);
|
||||
}
|
||||
|
||||
lec_send(vcc, skb, priv);
|
||||
lec_send(vcc, skb);
|
||||
|
||||
if (!atm_may_send(vcc, 0)) {
|
||||
struct lec_vcc_priv *vpriv = LEC_VCC_PRIV(vcc);
|
||||
@@ -427,15 +426,6 @@ static int lec_close(struct net_device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the current statistics.
|
||||
* This may be called with the card open or closed.
|
||||
*/
|
||||
static struct net_device_stats *lec_get_stats(struct net_device *dev)
|
||||
{
|
||||
return &((struct lec_priv *)netdev_priv(dev))->stats;
|
||||
}
|
||||
|
||||
static int lec_atm_send(struct atm_vcc *vcc, struct sk_buff *skb)
|
||||
{
|
||||
unsigned long flags;
|
||||
@@ -512,7 +502,7 @@ static int lec_atm_send(struct atm_vcc *vcc, struct sk_buff *skb)
|
||||
priv->lane2_ops = NULL;
|
||||
if (priv->lane_version > 1)
|
||||
priv->lane2_ops = &lane2_ops;
|
||||
if (dev->change_mtu(dev, mesg->content.config.mtu))
|
||||
if (dev_set_mtu(dev, mesg->content.config.mtu))
|
||||
printk("%s: change_mtu to %d failed\n", dev->name,
|
||||
mesg->content.config.mtu);
|
||||
priv->is_proxy = mesg->content.config.is_proxy;
|
||||
@@ -677,17 +667,19 @@ static void lec_set_multicast_list(struct net_device *dev)
|
||||
return;
|
||||
}
|
||||
|
||||
static const struct net_device_ops lec_netdev_ops = {
|
||||
.ndo_open = lec_open,
|
||||
.ndo_stop = lec_close,
|
||||
.ndo_start_xmit = lec_start_xmit,
|
||||
.ndo_change_mtu = lec_change_mtu,
|
||||
.ndo_tx_timeout = lec_tx_timeout,
|
||||
.ndo_set_multicast_list = lec_set_multicast_list,
|
||||
};
|
||||
|
||||
|
||||
static void lec_init(struct net_device *dev)
|
||||
{
|
||||
dev->change_mtu = lec_change_mtu;
|
||||
dev->open = lec_open;
|
||||
dev->stop = lec_close;
|
||||
dev->hard_start_xmit = lec_start_xmit;
|
||||
dev->tx_timeout = lec_tx_timeout;
|
||||
|
||||
dev->get_stats = lec_get_stats;
|
||||
dev->set_multicast_list = lec_set_multicast_list;
|
||||
dev->do_ioctl = NULL;
|
||||
dev->netdev_ops = &lec_netdev_ops;
|
||||
printk("%s: Initialized!\n", dev->name);
|
||||
}
|
||||
|
||||
@@ -810,8 +802,8 @@ static void lec_push(struct atm_vcc *vcc, struct sk_buff *skb)
|
||||
else
|
||||
#endif
|
||||
skb->protocol = eth_type_trans(skb, dev);
|
||||
priv->stats.rx_packets++;
|
||||
priv->stats.rx_bytes += skb->len;
|
||||
dev->stats.rx_packets++;
|
||||
dev->stats.rx_bytes += skb->len;
|
||||
memset(ATM_SKB(skb), 0, sizeof(struct atm_skb_data));
|
||||
netif_rx(skb);
|
||||
}
|
||||
@@ -1887,7 +1879,7 @@ restart:
|
||||
lec_arp_hold(entry);
|
||||
spin_unlock_irqrestore(&priv->lec_arp_lock, flags);
|
||||
while ((skb = skb_dequeue(&entry->tx_wait)) != NULL)
|
||||
lec_send(vcc, skb, entry->priv);
|
||||
lec_send(vcc, skb);
|
||||
entry->last_used = jiffies;
|
||||
entry->status = ESI_FORWARD_DIRECT;
|
||||
lec_arp_put(entry);
|
||||
@@ -2305,7 +2297,7 @@ restart:
|
||||
lec_arp_hold(entry);
|
||||
spin_unlock_irqrestore(&priv->lec_arp_lock, flags);
|
||||
while ((skb = skb_dequeue(&entry->tx_wait)) != NULL)
|
||||
lec_send(vcc, skb, entry->priv);
|
||||
lec_send(vcc, skb);
|
||||
entry->last_used = jiffies;
|
||||
entry->status = ESI_FORWARD_DIRECT;
|
||||
lec_arp_put(entry);
|
||||
|
||||
@@ -69,7 +69,6 @@ struct lane2_ops {
|
||||
#define LEC_ARP_TABLE_SIZE 16
|
||||
|
||||
struct lec_priv {
|
||||
struct net_device_stats stats;
|
||||
unsigned short lecid; /* Lecid of this client */
|
||||
struct hlist_head lec_arp_empty_ones;
|
||||
/* Used for storing VCC's that don't have a MAC address attached yet */
|
||||
|
||||
+14
-18
@@ -286,33 +286,32 @@ static void start_mpc(struct mpoa_client *mpc, struct net_device *dev)
|
||||
{
|
||||
|
||||
dprintk("mpoa: (%s) start_mpc:\n", mpc->dev->name);
|
||||
if (dev->hard_start_xmit == NULL) {
|
||||
printk("mpoa: (%s) start_mpc: dev->hard_start_xmit == NULL, not starting\n",
|
||||
dev->name);
|
||||
return;
|
||||
if (!dev->netdev_ops)
|
||||
printk("mpoa: (%s) start_mpc not starting\n", dev->name);
|
||||
else {
|
||||
mpc->old_ops = dev->netdev_ops;
|
||||
mpc->new_ops = *mpc->old_ops;
|
||||
mpc->new_ops.ndo_start_xmit = mpc_send_packet;
|
||||
dev->netdev_ops = &mpc->new_ops;
|
||||
}
|
||||
mpc->old_hard_start_xmit = dev->hard_start_xmit;
|
||||
dev->hard_start_xmit = mpc_send_packet;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void stop_mpc(struct mpoa_client *mpc)
|
||||
{
|
||||
|
||||
struct net_device *dev = mpc->dev;
|
||||
dprintk("mpoa: (%s) stop_mpc:", mpc->dev->name);
|
||||
|
||||
/* Lets not nullify lec device's dev->hard_start_xmit */
|
||||
if (mpc->dev->hard_start_xmit != mpc_send_packet) {
|
||||
if (dev->netdev_ops != &mpc->new_ops) {
|
||||
dprintk(" mpc already stopped, not fatal\n");
|
||||
return;
|
||||
}
|
||||
dprintk("\n");
|
||||
mpc->dev->hard_start_xmit = mpc->old_hard_start_xmit;
|
||||
mpc->old_hard_start_xmit = NULL;
|
||||
/* close_shortcuts(mpc); ??? FIXME */
|
||||
|
||||
return;
|
||||
dev->netdev_ops = mpc->old_ops;
|
||||
mpc->old_ops = NULL;
|
||||
|
||||
/* close_shortcuts(mpc); ??? FIXME */
|
||||
}
|
||||
|
||||
static const char *mpoa_device_type_string(char type) __attribute__ ((unused));
|
||||
@@ -531,7 +530,6 @@ static int send_via_shortcut(struct sk_buff *skb, struct mpoa_client *mpc)
|
||||
*/
|
||||
static int mpc_send_packet(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
int retval;
|
||||
struct mpoa_client *mpc;
|
||||
struct ethhdr *eth;
|
||||
int i = 0;
|
||||
@@ -561,9 +559,7 @@ static int mpc_send_packet(struct sk_buff *skb, struct net_device *dev)
|
||||
}
|
||||
|
||||
non_ip:
|
||||
retval = mpc->old_hard_start_xmit(skb,dev);
|
||||
|
||||
return retval;
|
||||
return mpc->old_ops->ndo_start_xmit(skb,dev);
|
||||
}
|
||||
|
||||
static int atm_mpoa_vcc_attach(struct atm_vcc *vcc, void __user *arg)
|
||||
|
||||
+4
-1
@@ -15,7 +15,7 @@ struct mpoa_client {
|
||||
struct mpoa_client *next;
|
||||
struct net_device *dev; /* lec in question */
|
||||
int dev_num; /* e.g. 2 for lec2 */
|
||||
int (*old_hard_start_xmit)(struct sk_buff *skb, struct net_device *dev);
|
||||
|
||||
struct atm_vcc *mpoad_vcc; /* control channel to mpoad */
|
||||
uint8_t mps_ctrl_addr[ATM_ESA_LEN]; /* MPS control ATM address */
|
||||
uint8_t our_ctrl_addr[ATM_ESA_LEN]; /* MPC's control ATM address */
|
||||
@@ -31,6 +31,9 @@ struct mpoa_client {
|
||||
uint8_t *mps_macs; /* array of MPS MAC addresses, >=1 */
|
||||
int number_of_mps_macs; /* number of the above MAC addresses */
|
||||
struct mpc_parameters parameters; /* parameters for this client */
|
||||
|
||||
const struct net_device_ops *old_ops;
|
||||
struct net_device_ops new_ops;
|
||||
};
|
||||
|
||||
|
||||
|
||||
+3
-6
@@ -1529,10 +1529,8 @@ static int ax25_sendmsg(struct kiocb *iocb, struct socket *sock,
|
||||
dp = ax25->digipeat;
|
||||
}
|
||||
|
||||
SOCK_DEBUG(sk, "AX.25: sendto: Addresses built.\n");
|
||||
|
||||
/* Build a packet */
|
||||
SOCK_DEBUG(sk, "AX.25: sendto: building packet.\n");
|
||||
SOCK_DEBUG(sk, "AX.25: sendto: Addresses built. Building packet.\n");
|
||||
|
||||
/* Assume the worst case */
|
||||
size = len + ax25->ax25_dev->dev->hard_header_len;
|
||||
@@ -1985,9 +1983,8 @@ static const struct proto_ops ax25_proto_ops = {
|
||||
/*
|
||||
* Called by socket.c on kernel start up
|
||||
*/
|
||||
static struct packet_type ax25_packet_type = {
|
||||
.type = __constant_htons(ETH_P_AX25),
|
||||
.dev = NULL, /* All devices */
|
||||
static struct packet_type ax25_packet_type __read_mostly = {
|
||||
.type = cpu_to_be16(ETH_P_AX25),
|
||||
.func = ax25_kiss_rcv,
|
||||
};
|
||||
|
||||
|
||||
@@ -61,27 +61,24 @@ void ax25_protocol_release(unsigned int pid)
|
||||
|
||||
write_lock_bh(&protocol_list_lock);
|
||||
protocol = protocol_list;
|
||||
if (protocol == NULL) {
|
||||
write_unlock_bh(&protocol_list_lock);
|
||||
return;
|
||||
}
|
||||
if (protocol == NULL)
|
||||
goto out;
|
||||
|
||||
if (protocol->pid == pid) {
|
||||
protocol_list = protocol->next;
|
||||
write_unlock_bh(&protocol_list_lock);
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
|
||||
while (protocol != NULL && protocol->next != NULL) {
|
||||
if (protocol->next->pid == pid) {
|
||||
s = protocol->next;
|
||||
protocol->next = protocol->next->next;
|
||||
write_unlock_bh(&protocol_list_lock);
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
|
||||
protocol = protocol->next;
|
||||
}
|
||||
out:
|
||||
write_unlock_bh(&protocol_list_lock);
|
||||
}
|
||||
|
||||
|
||||
@@ -41,14 +41,13 @@
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
|
||||
#define VERSION "2.14"
|
||||
#define VERSION "2.15"
|
||||
|
||||
/* Bluetooth sockets */
|
||||
#define BT_MAX_PROTO 8
|
||||
static struct net_proto_family *bt_proto[BT_MAX_PROTO];
|
||||
static DEFINE_RWLOCK(bt_proto_lock);
|
||||
|
||||
#ifdef CONFIG_DEBUG_LOCK_ALLOC
|
||||
static struct lock_class_key bt_lock_key[BT_MAX_PROTO];
|
||||
static const char *bt_key_strings[BT_MAX_PROTO] = {
|
||||
"sk_lock-AF_BLUETOOTH-BTPROTO_L2CAP",
|
||||
@@ -86,11 +85,6 @@ static inline void bt_sock_reclassify_lock(struct socket *sock, int proto)
|
||||
bt_slock_key_strings[proto], &bt_slock_key[proto],
|
||||
bt_key_strings[proto], &bt_lock_key[proto]);
|
||||
}
|
||||
#else
|
||||
static inline void bt_sock_reclassify_lock(struct socket *sock, int proto)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
int bt_sock_register(int proto, struct net_proto_family *ops)
|
||||
{
|
||||
@@ -217,7 +211,8 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sk->sk_state == BT_CONNECTED || !newsock) {
|
||||
if (sk->sk_state == BT_CONNECTED || !newsock ||
|
||||
bt_sk(parent)->defer_setup) {
|
||||
bt_accept_unlink(sk);
|
||||
if (newsock)
|
||||
sock_graft(sk, newsock);
|
||||
@@ -232,7 +227,7 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock)
|
||||
EXPORT_SYMBOL(bt_accept_dequeue);
|
||||
|
||||
int bt_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
|
||||
struct msghdr *msg, size_t len, int flags)
|
||||
struct msghdr *msg, size_t len, int flags)
|
||||
{
|
||||
int noblock = flags & MSG_DONTWAIT;
|
||||
struct sock *sk = sock->sk;
|
||||
@@ -277,7 +272,9 @@ static inline unsigned int bt_accept_poll(struct sock *parent)
|
||||
|
||||
list_for_each_safe(p, n, &bt_sk(parent)->accept_q) {
|
||||
sk = (struct sock *) list_entry(p, struct bt_sock, accept_q);
|
||||
if (sk->sk_state == BT_CONNECTED)
|
||||
if (sk->sk_state == BT_CONNECTED ||
|
||||
(bt_sk(parent)->defer_setup &&
|
||||
sk->sk_state == BT_CONNECT2))
|
||||
return POLLIN | POLLRDNORM;
|
||||
}
|
||||
|
||||
|
||||
@@ -126,8 +126,7 @@ static inline void cmtp_add_msgpart(struct cmtp_session *session, int id, const
|
||||
|
||||
session->reassembly[id] = nskb;
|
||||
|
||||
if (skb)
|
||||
kfree_skb(skb);
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
static inline int cmtp_recv_frame(struct cmtp_session *session, struct sk_buff *skb)
|
||||
|
||||
+41
-23
@@ -123,6 +123,8 @@ void hci_add_sco(struct hci_conn *conn, __u16 handle)
|
||||
conn->state = BT_CONNECT;
|
||||
conn->out = 1;
|
||||
|
||||
conn->attempt++;
|
||||
|
||||
cp.handle = cpu_to_le16(handle);
|
||||
cp.pkt_type = cpu_to_le16(conn->pkt_type);
|
||||
|
||||
@@ -139,6 +141,8 @@ void hci_setup_sync(struct hci_conn *conn, __u16 handle)
|
||||
conn->state = BT_CONNECT;
|
||||
conn->out = 1;
|
||||
|
||||
conn->attempt++;
|
||||
|
||||
cp.handle = cpu_to_le16(handle);
|
||||
cp.pkt_type = cpu_to_le16(conn->pkt_type);
|
||||
|
||||
@@ -155,6 +159,7 @@ static void hci_conn_timeout(unsigned long arg)
|
||||
{
|
||||
struct hci_conn *conn = (void *) arg;
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
__u8 reason;
|
||||
|
||||
BT_DBG("conn %p state %d", conn, conn->state);
|
||||
|
||||
@@ -173,7 +178,8 @@ static void hci_conn_timeout(unsigned long arg)
|
||||
break;
|
||||
case BT_CONFIG:
|
||||
case BT_CONNECTED:
|
||||
hci_acl_disconn(conn, 0x13);
|
||||
reason = hci_proto_disconn_ind(conn);
|
||||
hci_acl_disconn(conn, reason);
|
||||
break;
|
||||
default:
|
||||
conn->state = BT_CLOSED;
|
||||
@@ -216,12 +222,13 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
|
||||
break;
|
||||
case SCO_LINK:
|
||||
if (lmp_esco_capable(hdev))
|
||||
conn->pkt_type = hdev->esco_type & SCO_ESCO_MASK;
|
||||
conn->pkt_type = (hdev->esco_type & SCO_ESCO_MASK) |
|
||||
(hdev->esco_type & EDR_ESCO_MASK);
|
||||
else
|
||||
conn->pkt_type = hdev->pkt_type & SCO_PTYPE_MASK;
|
||||
break;
|
||||
case ESCO_LINK:
|
||||
conn->pkt_type = hdev->esco_type;
|
||||
conn->pkt_type = hdev->esco_type & ~EDR_ESCO_MASK;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -280,6 +287,8 @@ int hci_conn_del(struct hci_conn *conn)
|
||||
|
||||
skb_queue_purge(&conn->data_q);
|
||||
|
||||
hci_conn_del_sysfs(conn);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -325,7 +334,7 @@ EXPORT_SYMBOL(hci_get_route);
|
||||
|
||||
/* Create SCO or ACL connection.
|
||||
* Device _must_ be locked */
|
||||
struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 auth_type)
|
||||
struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 sec_level, __u8 auth_type)
|
||||
{
|
||||
struct hci_conn *acl;
|
||||
struct hci_conn *sco;
|
||||
@@ -340,6 +349,7 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8
|
||||
hci_conn_hold(acl);
|
||||
|
||||
if (acl->state == BT_OPEN || acl->state == BT_CLOSED) {
|
||||
acl->sec_level = sec_level;
|
||||
acl->auth_type = auth_type;
|
||||
hci_acl_connect(acl);
|
||||
}
|
||||
@@ -385,51 +395,59 @@ int hci_conn_check_link_mode(struct hci_conn *conn)
|
||||
EXPORT_SYMBOL(hci_conn_check_link_mode);
|
||||
|
||||
/* Authenticate remote device */
|
||||
int hci_conn_auth(struct hci_conn *conn)
|
||||
static int hci_conn_auth(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
|
||||
{
|
||||
BT_DBG("conn %p", conn);
|
||||
|
||||
if (conn->ssp_mode > 0 && conn->hdev->ssp_mode > 0) {
|
||||
if (!(conn->auth_type & 0x01)) {
|
||||
conn->auth_type |= 0x01;
|
||||
conn->link_mode &= ~HCI_LM_AUTH;
|
||||
}
|
||||
}
|
||||
|
||||
if (conn->link_mode & HCI_LM_AUTH)
|
||||
if (sec_level > conn->sec_level)
|
||||
conn->sec_level = sec_level;
|
||||
else if (conn->link_mode & HCI_LM_AUTH)
|
||||
return 1;
|
||||
|
||||
conn->auth_type = auth_type;
|
||||
|
||||
if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->pend)) {
|
||||
struct hci_cp_auth_requested cp;
|
||||
cp.handle = cpu_to_le16(conn->handle);
|
||||
hci_send_cmd(conn->hdev, HCI_OP_AUTH_REQUESTED,
|
||||
sizeof(cp), &cp);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(hci_conn_auth);
|
||||
|
||||
/* Enable encryption */
|
||||
int hci_conn_encrypt(struct hci_conn *conn)
|
||||
/* Enable security */
|
||||
int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
|
||||
{
|
||||
BT_DBG("conn %p", conn);
|
||||
|
||||
if (sec_level == BT_SECURITY_SDP)
|
||||
return 1;
|
||||
|
||||
if (sec_level == BT_SECURITY_LOW) {
|
||||
if (conn->ssp_mode > 0 && conn->hdev->ssp_mode > 0)
|
||||
return hci_conn_auth(conn, sec_level, auth_type);
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (conn->link_mode & HCI_LM_ENCRYPT)
|
||||
return hci_conn_auth(conn);
|
||||
return hci_conn_auth(conn, sec_level, auth_type);
|
||||
|
||||
if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend))
|
||||
return 0;
|
||||
|
||||
if (hci_conn_auth(conn)) {
|
||||
if (hci_conn_auth(conn, sec_level, auth_type)) {
|
||||
struct hci_cp_set_conn_encrypt cp;
|
||||
cp.handle = cpu_to_le16(conn->handle);
|
||||
cp.encrypt = 1;
|
||||
hci_send_cmd(conn->hdev, HCI_OP_SET_CONN_ENCRYPT,
|
||||
sizeof(cp), &cp);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(hci_conn_encrypt);
|
||||
EXPORT_SYMBOL(hci_conn_security);
|
||||
|
||||
/* Change link key */
|
||||
int hci_conn_change_link_key(struct hci_conn *conn)
|
||||
@@ -442,12 +460,13 @@ int hci_conn_change_link_key(struct hci_conn *conn)
|
||||
hci_send_cmd(conn->hdev, HCI_OP_CHANGE_CONN_LINK_KEY,
|
||||
sizeof(cp), &cp);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(hci_conn_change_link_key);
|
||||
|
||||
/* Switch role */
|
||||
int hci_conn_switch_role(struct hci_conn *conn, uint8_t role)
|
||||
int hci_conn_switch_role(struct hci_conn *conn, __u8 role)
|
||||
{
|
||||
BT_DBG("conn %p", conn);
|
||||
|
||||
@@ -460,6 +479,7 @@ int hci_conn_switch_role(struct hci_conn *conn, uint8_t role)
|
||||
cp.role = role;
|
||||
hci_send_cmd(conn->hdev, HCI_OP_SWITCH_ROLE, sizeof(cp), &cp);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(hci_conn_switch_role);
|
||||
@@ -542,9 +562,7 @@ void hci_conn_hash_flush(struct hci_dev *hdev)
|
||||
|
||||
c->state = BT_CLOSED;
|
||||
|
||||
hci_conn_del_sysfs(c);
|
||||
|
||||
hci_proto_disconn_ind(c, 0x16);
|
||||
hci_proto_disconn_cfm(c, 0x16);
|
||||
hci_conn_del(c);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1565,8 +1565,7 @@ static void hci_cmd_task(unsigned long arg)
|
||||
|
||||
/* Send queued commands */
|
||||
if (atomic_read(&hdev->cmd_cnt) && (skb = skb_dequeue(&hdev->cmd_q))) {
|
||||
if (hdev->sent_cmd)
|
||||
kfree_skb(hdev->sent_cmd);
|
||||
kfree_skb(hdev->sent_cmd);
|
||||
|
||||
if ((hdev->sent_cmd = skb_clone(skb, GFP_ATOMIC))) {
|
||||
atomic_dec(&hdev->cmd_cnt);
|
||||
|
||||
@@ -484,6 +484,15 @@ static void hci_cc_read_local_features(struct hci_dev *hdev, struct sk_buff *skb
|
||||
if (hdev->features[4] & LMP_EV5)
|
||||
hdev->esco_type |= (ESCO_EV5);
|
||||
|
||||
if (hdev->features[5] & LMP_EDR_ESCO_2M)
|
||||
hdev->esco_type |= (ESCO_2EV3);
|
||||
|
||||
if (hdev->features[5] & LMP_EDR_ESCO_3M)
|
||||
hdev->esco_type |= (ESCO_3EV3);
|
||||
|
||||
if (hdev->features[5] & LMP_EDR_3S_ESCO)
|
||||
hdev->esco_type |= (ESCO_2EV5 | ESCO_3EV5);
|
||||
|
||||
BT_DBG("%s features 0x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x", hdev->name,
|
||||
hdev->features[0], hdev->features[1],
|
||||
hdev->features[2], hdev->features[3],
|
||||
@@ -914,7 +923,8 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
|
||||
if (ev->status) {
|
||||
hci_proto_connect_cfm(conn, ev->status);
|
||||
hci_conn_del(conn);
|
||||
}
|
||||
} else if (ev->link_type != ACL_LINK)
|
||||
hci_proto_connect_cfm(conn, ev->status);
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
@@ -1009,9 +1019,7 @@ static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff
|
||||
if (conn) {
|
||||
conn->state = BT_CLOSED;
|
||||
|
||||
hci_conn_del_sysfs(conn);
|
||||
|
||||
hci_proto_disconn_ind(conn, ev->reason);
|
||||
hci_proto_disconn_cfm(conn, ev->reason);
|
||||
hci_conn_del(conn);
|
||||
}
|
||||
|
||||
@@ -1600,7 +1608,8 @@ static inline void hci_remote_ext_features_evt(struct hci_dev *hdev, struct sk_b
|
||||
|
||||
if (conn->state == BT_CONFIG) {
|
||||
if (!ev->status && hdev->ssp_mode > 0 &&
|
||||
conn->ssp_mode > 0 && conn->out) {
|
||||
conn->ssp_mode > 0 && conn->out &&
|
||||
conn->sec_level != BT_SECURITY_SDP) {
|
||||
struct hci_cp_auth_requested cp;
|
||||
cp.handle = ev->handle;
|
||||
hci_send_cmd(hdev, HCI_OP_AUTH_REQUESTED,
|
||||
@@ -1637,6 +1646,13 @@ static inline void hci_sync_conn_complete_evt(struct hci_dev *hdev, struct sk_bu
|
||||
conn->type = SCO_LINK;
|
||||
}
|
||||
|
||||
if (conn->out && ev->status == 0x1c && conn->attempt < 2) {
|
||||
conn->pkt_type = (hdev->esco_type & SCO_ESCO_MASK) |
|
||||
(hdev->esco_type & EDR_ESCO_MASK);
|
||||
hci_setup_sync(conn, conn->link->handle);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (!ev->status) {
|
||||
conn->handle = __le16_to_cpu(ev->handle);
|
||||
conn->state = BT_CONNECTED;
|
||||
|
||||
@@ -140,7 +140,7 @@ static void del_conn(struct work_struct *work)
|
||||
dev = device_find_child(&conn->dev, NULL, __match_tty);
|
||||
if (!dev)
|
||||
break;
|
||||
device_move(dev, NULL);
|
||||
device_move(dev, NULL, DPM_ORDER_DEV_LAST);
|
||||
put_device(dev);
|
||||
}
|
||||
|
||||
|
||||
+441
-163
@@ -50,9 +50,10 @@
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
#include <net/bluetooth/l2cap.h>
|
||||
|
||||
#define VERSION "2.11"
|
||||
#define VERSION "2.13"
|
||||
|
||||
static u32 l2cap_feat_mask = 0x0000;
|
||||
static u32 l2cap_feat_mask = 0x0080;
|
||||
static u8 l2cap_fixed_chan[8] = { 0x02, };
|
||||
|
||||
static const struct proto_ops l2cap_sock_ops;
|
||||
|
||||
@@ -77,9 +78,10 @@ static void l2cap_sock_timeout(unsigned long arg)
|
||||
|
||||
bh_lock_sock(sk);
|
||||
|
||||
if (sk->sk_state == BT_CONNECT &&
|
||||
(l2cap_pi(sk)->link_mode & (L2CAP_LM_AUTH |
|
||||
L2CAP_LM_ENCRYPT | L2CAP_LM_SECURE)))
|
||||
if (sk->sk_state == BT_CONNECTED || sk->sk_state == BT_CONFIG)
|
||||
reason = ECONNREFUSED;
|
||||
else if (sk->sk_state == BT_CONNECT &&
|
||||
l2cap_pi(sk)->sec_level != BT_SECURITY_SDP)
|
||||
reason = ECONNREFUSED;
|
||||
else
|
||||
reason = ETIMEDOUT;
|
||||
@@ -204,6 +206,8 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct so
|
||||
|
||||
BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn, l2cap_pi(sk)->psm, l2cap_pi(sk)->dcid);
|
||||
|
||||
conn->disc_reason = 0x13;
|
||||
|
||||
l2cap_pi(sk)->conn = conn;
|
||||
|
||||
if (sk->sk_type == SOCK_SEQPACKET) {
|
||||
@@ -259,18 +263,35 @@ static void l2cap_chan_del(struct sock *sk, int err)
|
||||
}
|
||||
|
||||
/* Service level security */
|
||||
static inline int l2cap_check_link_mode(struct sock *sk)
|
||||
static inline int l2cap_check_security(struct sock *sk)
|
||||
{
|
||||
struct l2cap_conn *conn = l2cap_pi(sk)->conn;
|
||||
__u8 auth_type;
|
||||
|
||||
if ((l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT) ||
|
||||
(l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE))
|
||||
return hci_conn_encrypt(conn->hcon);
|
||||
if (l2cap_pi(sk)->psm == cpu_to_le16(0x0001)) {
|
||||
if (l2cap_pi(sk)->sec_level == BT_SECURITY_HIGH)
|
||||
auth_type = HCI_AT_NO_BONDING_MITM;
|
||||
else
|
||||
auth_type = HCI_AT_NO_BONDING;
|
||||
|
||||
if (l2cap_pi(sk)->link_mode & L2CAP_LM_AUTH)
|
||||
return hci_conn_auth(conn->hcon);
|
||||
if (l2cap_pi(sk)->sec_level == BT_SECURITY_LOW)
|
||||
l2cap_pi(sk)->sec_level = BT_SECURITY_SDP;
|
||||
} else {
|
||||
switch (l2cap_pi(sk)->sec_level) {
|
||||
case BT_SECURITY_HIGH:
|
||||
auth_type = HCI_AT_GENERAL_BONDING_MITM;
|
||||
break;
|
||||
case BT_SECURITY_MEDIUM:
|
||||
auth_type = HCI_AT_GENERAL_BONDING;
|
||||
break;
|
||||
default:
|
||||
auth_type = HCI_AT_NO_BONDING;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
return hci_conn_security(conn->hcon, l2cap_pi(sk)->sec_level,
|
||||
auth_type);
|
||||
}
|
||||
|
||||
static inline u8 l2cap_get_ident(struct l2cap_conn *conn)
|
||||
@@ -312,7 +333,10 @@ static void l2cap_do_start(struct sock *sk)
|
||||
struct l2cap_conn *conn = l2cap_pi(sk)->conn;
|
||||
|
||||
if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) {
|
||||
if (l2cap_check_link_mode(sk)) {
|
||||
if (!(conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE))
|
||||
return;
|
||||
|
||||
if (l2cap_check_security(sk)) {
|
||||
struct l2cap_conn_req req;
|
||||
req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
|
||||
req.psm = l2cap_pi(sk)->psm;
|
||||
@@ -356,7 +380,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
|
||||
}
|
||||
|
||||
if (sk->sk_state == BT_CONNECT) {
|
||||
if (l2cap_check_link_mode(sk)) {
|
||||
if (l2cap_check_security(sk)) {
|
||||
struct l2cap_conn_req req;
|
||||
req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
|
||||
req.psm = l2cap_pi(sk)->psm;
|
||||
@@ -371,10 +395,18 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
|
||||
rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid);
|
||||
rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid);
|
||||
|
||||
if (l2cap_check_link_mode(sk)) {
|
||||
sk->sk_state = BT_CONFIG;
|
||||
rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS);
|
||||
rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
|
||||
if (l2cap_check_security(sk)) {
|
||||
if (bt_sk(sk)->defer_setup) {
|
||||
struct sock *parent = bt_sk(sk)->parent;
|
||||
rsp.result = cpu_to_le16(L2CAP_CR_PEND);
|
||||
rsp.status = cpu_to_le16(L2CAP_CS_AUTHOR_PEND);
|
||||
parent->sk_data_ready(parent, 0);
|
||||
|
||||
} else {
|
||||
sk->sk_state = BT_CONFIG;
|
||||
rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS);
|
||||
rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
|
||||
}
|
||||
} else {
|
||||
rsp.result = cpu_to_le16(L2CAP_CR_PEND);
|
||||
rsp.status = cpu_to_le16(L2CAP_CS_AUTHEN_PEND);
|
||||
@@ -426,7 +458,7 @@ static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err)
|
||||
read_lock(&l->lock);
|
||||
|
||||
for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
|
||||
if (l2cap_pi(sk)->link_mode & L2CAP_LM_RELIABLE)
|
||||
if (l2cap_pi(sk)->force_reliable)
|
||||
sk->sk_err = err;
|
||||
}
|
||||
|
||||
@@ -437,6 +469,7 @@ static void l2cap_info_timeout(unsigned long arg)
|
||||
{
|
||||
struct l2cap_conn *conn = (void *) arg;
|
||||
|
||||
conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
|
||||
conn->info_ident = 0;
|
||||
|
||||
l2cap_conn_start(conn);
|
||||
@@ -470,6 +503,8 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status)
|
||||
spin_lock_init(&conn->lock);
|
||||
rwlock_init(&conn->chan_list.lock);
|
||||
|
||||
conn->disc_reason = 0x13;
|
||||
|
||||
return conn;
|
||||
}
|
||||
|
||||
@@ -483,8 +518,7 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err)
|
||||
|
||||
BT_DBG("hcon %p conn %p, err %d", hcon, conn, err);
|
||||
|
||||
if (conn->rx_skb)
|
||||
kfree_skb(conn->rx_skb);
|
||||
kfree_skb(conn->rx_skb);
|
||||
|
||||
/* Kill channels */
|
||||
while ((sk = conn->chan_list.head)) {
|
||||
@@ -608,7 +642,6 @@ static void __l2cap_sock_close(struct sock *sk, int reason)
|
||||
|
||||
case BT_CONNECTED:
|
||||
case BT_CONFIG:
|
||||
case BT_CONNECT2:
|
||||
if (sk->sk_type == SOCK_SEQPACKET) {
|
||||
struct l2cap_conn *conn = l2cap_pi(sk)->conn;
|
||||
struct l2cap_disconn_req req;
|
||||
@@ -624,6 +657,27 @@ static void __l2cap_sock_close(struct sock *sk, int reason)
|
||||
l2cap_chan_del(sk, reason);
|
||||
break;
|
||||
|
||||
case BT_CONNECT2:
|
||||
if (sk->sk_type == SOCK_SEQPACKET) {
|
||||
struct l2cap_conn *conn = l2cap_pi(sk)->conn;
|
||||
struct l2cap_conn_rsp rsp;
|
||||
__u16 result;
|
||||
|
||||
if (bt_sk(sk)->defer_setup)
|
||||
result = L2CAP_CR_SEC_BLOCK;
|
||||
else
|
||||
result = L2CAP_CR_BAD_PSM;
|
||||
|
||||
rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid);
|
||||
rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid);
|
||||
rsp.result = cpu_to_le16(result);
|
||||
rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
|
||||
l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
|
||||
L2CAP_CONN_RSP, sizeof(rsp), &rsp);
|
||||
} else
|
||||
l2cap_chan_del(sk, reason);
|
||||
break;
|
||||
|
||||
case BT_CONNECT:
|
||||
case BT_DISCONN:
|
||||
l2cap_chan_del(sk, reason);
|
||||
@@ -653,13 +707,19 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)
|
||||
|
||||
if (parent) {
|
||||
sk->sk_type = parent->sk_type;
|
||||
bt_sk(sk)->defer_setup = bt_sk(parent)->defer_setup;
|
||||
|
||||
pi->imtu = l2cap_pi(parent)->imtu;
|
||||
pi->omtu = l2cap_pi(parent)->omtu;
|
||||
pi->link_mode = l2cap_pi(parent)->link_mode;
|
||||
pi->sec_level = l2cap_pi(parent)->sec_level;
|
||||
pi->role_switch = l2cap_pi(parent)->role_switch;
|
||||
pi->force_reliable = l2cap_pi(parent)->force_reliable;
|
||||
} else {
|
||||
pi->imtu = L2CAP_DEFAULT_MTU;
|
||||
pi->omtu = 0;
|
||||
pi->link_mode = 0;
|
||||
pi->sec_level = BT_SECURITY_LOW;
|
||||
pi->role_switch = 0;
|
||||
pi->force_reliable = 0;
|
||||
}
|
||||
|
||||
/* Default config options */
|
||||
@@ -723,17 +783,24 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
|
||||
static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
|
||||
{
|
||||
struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr;
|
||||
struct sock *sk = sock->sk;
|
||||
int err = 0;
|
||||
struct sockaddr_l2 la;
|
||||
int len, err = 0;
|
||||
|
||||
BT_DBG("sk %p, %s %d", sk, batostr(&la->l2_bdaddr), la->l2_psm);
|
||||
BT_DBG("sk %p", sk);
|
||||
|
||||
if (!addr || addr->sa_family != AF_BLUETOOTH)
|
||||
return -EINVAL;
|
||||
|
||||
memset(&la, 0, sizeof(la));
|
||||
len = min_t(unsigned int, sizeof(la), alen);
|
||||
memcpy(&la, addr, len);
|
||||
|
||||
if (la.l2_cid)
|
||||
return -EINVAL;
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
if (sk->sk_state != BT_OPEN) {
|
||||
@@ -741,7 +808,7 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (la->l2_psm && btohs(la->l2_psm) < 0x1001 &&
|
||||
if (la.l2_psm && btohs(la.l2_psm) < 0x1001 &&
|
||||
!capable(CAP_NET_BIND_SERVICE)) {
|
||||
err = -EACCES;
|
||||
goto done;
|
||||
@@ -749,14 +816,17 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_
|
||||
|
||||
write_lock_bh(&l2cap_sk_list.lock);
|
||||
|
||||
if (la->l2_psm && __l2cap_get_sock_by_addr(la->l2_psm, &la->l2_bdaddr)) {
|
||||
if (la.l2_psm && __l2cap_get_sock_by_addr(la.l2_psm, &la.l2_bdaddr)) {
|
||||
err = -EADDRINUSE;
|
||||
} else {
|
||||
/* Save source address */
|
||||
bacpy(&bt_sk(sk)->src, &la->l2_bdaddr);
|
||||
l2cap_pi(sk)->psm = la->l2_psm;
|
||||
l2cap_pi(sk)->sport = la->l2_psm;
|
||||
bacpy(&bt_sk(sk)->src, &la.l2_bdaddr);
|
||||
l2cap_pi(sk)->psm = la.l2_psm;
|
||||
l2cap_pi(sk)->sport = la.l2_psm;
|
||||
sk->sk_state = BT_BOUND;
|
||||
|
||||
if (btohs(la.l2_psm) == 0x0001 || btohs(la.l2_psm) == 0x0003)
|
||||
l2cap_pi(sk)->sec_level = BT_SECURITY_SDP;
|
||||
}
|
||||
|
||||
write_unlock_bh(&l2cap_sk_list.lock);
|
||||
@@ -776,7 +846,8 @@ static int l2cap_do_connect(struct sock *sk)
|
||||
__u8 auth_type;
|
||||
int err = 0;
|
||||
|
||||
BT_DBG("%s -> %s psm 0x%2.2x", batostr(src), batostr(dst), l2cap_pi(sk)->psm);
|
||||
BT_DBG("%s -> %s psm 0x%2.2x", batostr(src), batostr(dst),
|
||||
l2cap_pi(sk)->psm);
|
||||
|
||||
if (!(hdev = hci_get_route(dst, src)))
|
||||
return -EHOSTUNREACH;
|
||||
@@ -785,21 +856,42 @@ static int l2cap_do_connect(struct sock *sk)
|
||||
|
||||
err = -ENOMEM;
|
||||
|
||||
if (l2cap_pi(sk)->link_mode & L2CAP_LM_AUTH ||
|
||||
l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT ||
|
||||
l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE) {
|
||||
if (l2cap_pi(sk)->psm == cpu_to_le16(0x0001))
|
||||
if (sk->sk_type == SOCK_RAW) {
|
||||
switch (l2cap_pi(sk)->sec_level) {
|
||||
case BT_SECURITY_HIGH:
|
||||
auth_type = HCI_AT_DEDICATED_BONDING_MITM;
|
||||
break;
|
||||
case BT_SECURITY_MEDIUM:
|
||||
auth_type = HCI_AT_DEDICATED_BONDING;
|
||||
break;
|
||||
default:
|
||||
auth_type = HCI_AT_NO_BONDING;
|
||||
break;
|
||||
}
|
||||
} else if (l2cap_pi(sk)->psm == cpu_to_le16(0x0001)) {
|
||||
if (l2cap_pi(sk)->sec_level == BT_SECURITY_HIGH)
|
||||
auth_type = HCI_AT_NO_BONDING_MITM;
|
||||
else
|
||||
auth_type = HCI_AT_GENERAL_BONDING_MITM;
|
||||
} else {
|
||||
if (l2cap_pi(sk)->psm == cpu_to_le16(0x0001))
|
||||
auth_type = HCI_AT_NO_BONDING;
|
||||
else
|
||||
|
||||
if (l2cap_pi(sk)->sec_level == BT_SECURITY_LOW)
|
||||
l2cap_pi(sk)->sec_level = BT_SECURITY_SDP;
|
||||
} else {
|
||||
switch (l2cap_pi(sk)->sec_level) {
|
||||
case BT_SECURITY_HIGH:
|
||||
auth_type = HCI_AT_GENERAL_BONDING_MITM;
|
||||
break;
|
||||
case BT_SECURITY_MEDIUM:
|
||||
auth_type = HCI_AT_GENERAL_BONDING;
|
||||
break;
|
||||
default:
|
||||
auth_type = HCI_AT_NO_BONDING;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
hcon = hci_connect(hdev, ACL_LINK, dst, auth_type);
|
||||
hcon = hci_connect(hdev, ACL_LINK, dst,
|
||||
l2cap_pi(sk)->sec_level, auth_type);
|
||||
if (!hcon)
|
||||
goto done;
|
||||
|
||||
@@ -835,20 +927,25 @@ done:
|
||||
|
||||
static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags)
|
||||
{
|
||||
struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr;
|
||||
struct sock *sk = sock->sk;
|
||||
int err = 0;
|
||||
|
||||
lock_sock(sk);
|
||||
struct sockaddr_l2 la;
|
||||
int len, err = 0;
|
||||
|
||||
BT_DBG("sk %p", sk);
|
||||
|
||||
if (addr->sa_family != AF_BLUETOOTH || alen < sizeof(struct sockaddr_l2)) {
|
||||
err = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
if (!addr || addr->sa_family != AF_BLUETOOTH)
|
||||
return -EINVAL;
|
||||
|
||||
if (sk->sk_type == SOCK_SEQPACKET && !la->l2_psm) {
|
||||
memset(&la, 0, sizeof(la));
|
||||
len = min_t(unsigned int, sizeof(la), alen);
|
||||
memcpy(&la, addr, len);
|
||||
|
||||
if (la.l2_cid)
|
||||
return -EINVAL;
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
if (sk->sk_type == SOCK_SEQPACKET && !la.l2_psm) {
|
||||
err = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
@@ -875,8 +972,8 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al
|
||||
}
|
||||
|
||||
/* Set destination address and psm */
|
||||
bacpy(&bt_sk(sk)->dst, &la->l2_bdaddr);
|
||||
l2cap_pi(sk)->psm = la->l2_psm;
|
||||
bacpy(&bt_sk(sk)->dst, &la.l2_bdaddr);
|
||||
l2cap_pi(sk)->psm = la.l2_psm;
|
||||
|
||||
if ((err = l2cap_do_connect(sk)))
|
||||
goto done;
|
||||
@@ -1000,12 +1097,16 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *l
|
||||
addr->sa_family = AF_BLUETOOTH;
|
||||
*len = sizeof(struct sockaddr_l2);
|
||||
|
||||
if (peer)
|
||||
if (peer) {
|
||||
la->l2_psm = l2cap_pi(sk)->psm;
|
||||
bacpy(&la->l2_bdaddr, &bt_sk(sk)->dst);
|
||||
else
|
||||
la->l2_cid = htobs(l2cap_pi(sk)->dcid);
|
||||
} else {
|
||||
la->l2_psm = l2cap_pi(sk)->sport;
|
||||
bacpy(&la->l2_bdaddr, &bt_sk(sk)->src);
|
||||
la->l2_cid = htobs(l2cap_pi(sk)->scid);
|
||||
}
|
||||
|
||||
la->l2_psm = l2cap_pi(sk)->psm;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1106,11 +1207,38 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms
|
||||
return err;
|
||||
}
|
||||
|
||||
static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, int optlen)
|
||||
static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len, int flags)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
if (sk->sk_state == BT_CONNECT2 && bt_sk(sk)->defer_setup) {
|
||||
struct l2cap_conn_rsp rsp;
|
||||
|
||||
sk->sk_state = BT_CONFIG;
|
||||
|
||||
rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid);
|
||||
rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid);
|
||||
rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS);
|
||||
rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
|
||||
l2cap_send_cmd(l2cap_pi(sk)->conn, l2cap_pi(sk)->ident,
|
||||
L2CAP_CONN_RSP, sizeof(rsp), &rsp);
|
||||
|
||||
release_sock(sk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
release_sock(sk);
|
||||
|
||||
return bt_sock_recvmsg(iocb, sock, msg, len, flags);
|
||||
}
|
||||
|
||||
static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __user *optval, int optlen)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
struct l2cap_options opts;
|
||||
int err = 0, len;
|
||||
int len, err = 0;
|
||||
u32 opt;
|
||||
|
||||
BT_DBG("sk %p", sk);
|
||||
@@ -1140,7 +1268,15 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
|
||||
break;
|
||||
}
|
||||
|
||||
l2cap_pi(sk)->link_mode = opt;
|
||||
if (opt & L2CAP_LM_AUTH)
|
||||
l2cap_pi(sk)->sec_level = BT_SECURITY_LOW;
|
||||
if (opt & L2CAP_LM_ENCRYPT)
|
||||
l2cap_pi(sk)->sec_level = BT_SECURITY_MEDIUM;
|
||||
if (opt & L2CAP_LM_SECURE)
|
||||
l2cap_pi(sk)->sec_level = BT_SECURITY_HIGH;
|
||||
|
||||
l2cap_pi(sk)->role_switch = (opt & L2CAP_LM_MASTER);
|
||||
l2cap_pi(sk)->force_reliable = (opt & L2CAP_LM_RELIABLE);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -1152,12 +1288,77 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
|
||||
return err;
|
||||
}
|
||||
|
||||
static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen)
|
||||
static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, int optlen)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
struct bt_security sec;
|
||||
int len, err = 0;
|
||||
u32 opt;
|
||||
|
||||
BT_DBG("sk %p", sk);
|
||||
|
||||
if (level == SOL_L2CAP)
|
||||
return l2cap_sock_setsockopt_old(sock, optname, optval, optlen);
|
||||
|
||||
if (level != SOL_BLUETOOTH)
|
||||
return -ENOPROTOOPT;
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
switch (optname) {
|
||||
case BT_SECURITY:
|
||||
if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_RAW) {
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
sec.level = BT_SECURITY_LOW;
|
||||
|
||||
len = min_t(unsigned int, sizeof(sec), optlen);
|
||||
if (copy_from_user((char *) &sec, optval, len)) {
|
||||
err = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
if (sec.level < BT_SECURITY_LOW ||
|
||||
sec.level > BT_SECURITY_HIGH) {
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
l2cap_pi(sk)->sec_level = sec.level;
|
||||
break;
|
||||
|
||||
case BT_DEFER_SETUP:
|
||||
if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) {
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (get_user(opt, (u32 __user *) optval)) {
|
||||
err = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
bt_sk(sk)->defer_setup = opt;
|
||||
break;
|
||||
|
||||
default:
|
||||
err = -ENOPROTOOPT;
|
||||
break;
|
||||
}
|
||||
|
||||
release_sock(sk);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __user *optval, int __user *optlen)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
struct l2cap_options opts;
|
||||
struct l2cap_conninfo cinfo;
|
||||
int len, err = 0;
|
||||
u32 opt;
|
||||
|
||||
BT_DBG("sk %p", sk);
|
||||
|
||||
@@ -1180,12 +1381,36 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch
|
||||
break;
|
||||
|
||||
case L2CAP_LM:
|
||||
if (put_user(l2cap_pi(sk)->link_mode, (u32 __user *) optval))
|
||||
switch (l2cap_pi(sk)->sec_level) {
|
||||
case BT_SECURITY_LOW:
|
||||
opt = L2CAP_LM_AUTH;
|
||||
break;
|
||||
case BT_SECURITY_MEDIUM:
|
||||
opt = L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT;
|
||||
break;
|
||||
case BT_SECURITY_HIGH:
|
||||
opt = L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT |
|
||||
L2CAP_LM_SECURE;
|
||||
break;
|
||||
default:
|
||||
opt = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (l2cap_pi(sk)->role_switch)
|
||||
opt |= L2CAP_LM_MASTER;
|
||||
|
||||
if (l2cap_pi(sk)->force_reliable)
|
||||
opt |= L2CAP_LM_RELIABLE;
|
||||
|
||||
if (put_user(opt, (u32 __user *) optval))
|
||||
err = -EFAULT;
|
||||
break;
|
||||
|
||||
case L2CAP_CONNINFO:
|
||||
if (sk->sk_state != BT_CONNECTED) {
|
||||
if (sk->sk_state != BT_CONNECTED &&
|
||||
!(sk->sk_state == BT_CONNECT2 &&
|
||||
bt_sk(sk)->defer_setup)) {
|
||||
err = -ENOTCONN;
|
||||
break;
|
||||
}
|
||||
@@ -1208,6 +1433,60 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch
|
||||
return err;
|
||||
}
|
||||
|
||||
static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
struct bt_security sec;
|
||||
int len, err = 0;
|
||||
|
||||
BT_DBG("sk %p", sk);
|
||||
|
||||
if (level == SOL_L2CAP)
|
||||
return l2cap_sock_getsockopt_old(sock, optname, optval, optlen);
|
||||
|
||||
if (level != SOL_BLUETOOTH)
|
||||
return -ENOPROTOOPT;
|
||||
|
||||
if (get_user(len, optlen))
|
||||
return -EFAULT;
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
switch (optname) {
|
||||
case BT_SECURITY:
|
||||
if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_RAW) {
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
sec.level = l2cap_pi(sk)->sec_level;
|
||||
|
||||
len = min_t(unsigned int, len, sizeof(sec));
|
||||
if (copy_to_user(optval, (char *) &sec, len))
|
||||
err = -EFAULT;
|
||||
|
||||
break;
|
||||
|
||||
case BT_DEFER_SETUP:
|
||||
if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) {
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (put_user(bt_sk(sk)->defer_setup, (u32 __user *) optval))
|
||||
err = -EFAULT;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
err = -ENOPROTOOPT;
|
||||
break;
|
||||
}
|
||||
|
||||
release_sock(sk);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int l2cap_sock_shutdown(struct socket *sock, int how)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
@@ -1270,11 +1549,6 @@ static void l2cap_chan_ready(struct sock *sk)
|
||||
*/
|
||||
parent->sk_data_ready(parent, 0);
|
||||
}
|
||||
|
||||
if (l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE) {
|
||||
struct l2cap_conn *conn = l2cap_pi(sk)->conn;
|
||||
hci_conn_change_link_key(conn->hcon);
|
||||
}
|
||||
}
|
||||
|
||||
/* Copy frame to all raw sockets on that connection */
|
||||
@@ -1549,8 +1823,11 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn, struct l2cap_cmd_hd
|
||||
|
||||
if ((conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) &&
|
||||
cmd->ident == conn->info_ident) {
|
||||
conn->info_ident = 0;
|
||||
del_timer(&conn->info_timer);
|
||||
|
||||
conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
|
||||
conn->info_ident = 0;
|
||||
|
||||
l2cap_conn_start(conn);
|
||||
}
|
||||
|
||||
@@ -1580,6 +1857,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
|
||||
/* Check if the ACL is secure enough (if not SDP) */
|
||||
if (psm != cpu_to_le16(0x0001) &&
|
||||
!hci_conn_check_link_mode(conn->hcon)) {
|
||||
conn->disc_reason = 0x05;
|
||||
result = L2CAP_CR_SEC_BLOCK;
|
||||
goto response;
|
||||
}
|
||||
@@ -1621,11 +1899,18 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
|
||||
|
||||
l2cap_pi(sk)->ident = cmd->ident;
|
||||
|
||||
if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) {
|
||||
if (l2cap_check_link_mode(sk)) {
|
||||
sk->sk_state = BT_CONFIG;
|
||||
result = L2CAP_CR_SUCCESS;
|
||||
status = L2CAP_CS_NO_INFO;
|
||||
if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE) {
|
||||
if (l2cap_check_security(sk)) {
|
||||
if (bt_sk(sk)->defer_setup) {
|
||||
sk->sk_state = BT_CONNECT2;
|
||||
result = L2CAP_CR_PEND;
|
||||
status = L2CAP_CS_AUTHOR_PEND;
|
||||
parent->sk_data_ready(parent, 0);
|
||||
} else {
|
||||
sk->sk_state = BT_CONFIG;
|
||||
result = L2CAP_CR_SUCCESS;
|
||||
status = L2CAP_CS_NO_INFO;
|
||||
}
|
||||
} else {
|
||||
sk->sk_state = BT_CONNECT2;
|
||||
result = L2CAP_CR_PEND;
|
||||
@@ -1695,11 +1980,14 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd
|
||||
l2cap_pi(sk)->dcid = dcid;
|
||||
l2cap_pi(sk)->conf_state |= L2CAP_CONF_REQ_SENT;
|
||||
|
||||
l2cap_pi(sk)->conf_state &= ~L2CAP_CONF_CONNECT_PEND;
|
||||
|
||||
l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
|
||||
l2cap_build_conf_req(sk, req), req);
|
||||
break;
|
||||
|
||||
case L2CAP_CR_PEND:
|
||||
l2cap_pi(sk)->conf_state |= L2CAP_CONF_CONNECT_PEND;
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -1908,6 +2196,14 @@ static inline int l2cap_information_req(struct l2cap_conn *conn, struct l2cap_cm
|
||||
put_unaligned(cpu_to_le32(l2cap_feat_mask), (__le32 *) rsp->data);
|
||||
l2cap_send_cmd(conn, cmd->ident,
|
||||
L2CAP_INFO_RSP, sizeof(buf), buf);
|
||||
} else if (type == L2CAP_IT_FIXED_CHAN) {
|
||||
u8 buf[12];
|
||||
struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) buf;
|
||||
rsp->type = cpu_to_le16(L2CAP_IT_FIXED_CHAN);
|
||||
rsp->result = cpu_to_le16(L2CAP_IR_SUCCESS);
|
||||
memcpy(buf + 4, l2cap_fixed_chan, 8);
|
||||
l2cap_send_cmd(conn, cmd->ident,
|
||||
L2CAP_INFO_RSP, sizeof(buf), buf);
|
||||
} else {
|
||||
struct l2cap_info_rsp rsp;
|
||||
rsp.type = cpu_to_le16(type);
|
||||
@@ -1929,14 +2225,31 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cm
|
||||
|
||||
BT_DBG("type 0x%4.4x result 0x%2.2x", type, result);
|
||||
|
||||
conn->info_ident = 0;
|
||||
|
||||
del_timer(&conn->info_timer);
|
||||
|
||||
if (type == L2CAP_IT_FEAT_MASK)
|
||||
if (type == L2CAP_IT_FEAT_MASK) {
|
||||
conn->feat_mask = get_unaligned_le32(rsp->data);
|
||||
|
||||
l2cap_conn_start(conn);
|
||||
if (conn->feat_mask & 0x0080) {
|
||||
struct l2cap_info_req req;
|
||||
req.type = cpu_to_le16(L2CAP_IT_FIXED_CHAN);
|
||||
|
||||
conn->info_ident = l2cap_get_ident(conn);
|
||||
|
||||
l2cap_send_cmd(conn, conn->info_ident,
|
||||
L2CAP_INFO_REQ, sizeof(req), &req);
|
||||
} else {
|
||||
conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
|
||||
conn->info_ident = 0;
|
||||
|
||||
l2cap_conn_start(conn);
|
||||
}
|
||||
} else if (type == L2CAP_IT_FIXED_CHAN) {
|
||||
conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
|
||||
conn->info_ident = 0;
|
||||
|
||||
l2cap_conn_start(conn);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -2143,10 +2456,15 @@ static int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
|
||||
continue;
|
||||
|
||||
if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr)) {
|
||||
lm1 |= (HCI_LM_ACCEPT | l2cap_pi(sk)->link_mode);
|
||||
lm1 |= HCI_LM_ACCEPT;
|
||||
if (l2cap_pi(sk)->role_switch)
|
||||
lm1 |= HCI_LM_MASTER;
|
||||
exact++;
|
||||
} else if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY))
|
||||
lm2 |= (HCI_LM_ACCEPT | l2cap_pi(sk)->link_mode);
|
||||
} else if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) {
|
||||
lm2 |= HCI_LM_ACCEPT;
|
||||
if (l2cap_pi(sk)->role_switch)
|
||||
lm2 |= HCI_LM_MASTER;
|
||||
}
|
||||
}
|
||||
read_unlock(&l2cap_sk_list.lock);
|
||||
|
||||
@@ -2172,7 +2490,19 @@ static int l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l2cap_disconn_ind(struct hci_conn *hcon, u8 reason)
|
||||
static int l2cap_disconn_ind(struct hci_conn *hcon)
|
||||
{
|
||||
struct l2cap_conn *conn = hcon->l2cap_data;
|
||||
|
||||
BT_DBG("hcon %p", hcon);
|
||||
|
||||
if (hcon->type != ACL_LINK || !conn)
|
||||
return 0x13;
|
||||
|
||||
return conn->disc_reason;
|
||||
}
|
||||
|
||||
static int l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason)
|
||||
{
|
||||
BT_DBG("hcon %p reason %d", hcon, reason);
|
||||
|
||||
@@ -2184,7 +2514,24 @@ static int l2cap_disconn_ind(struct hci_conn *hcon, u8 reason)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l2cap_auth_cfm(struct hci_conn *hcon, u8 status)
|
||||
static inline void l2cap_check_encryption(struct sock *sk, u8 encrypt)
|
||||
{
|
||||
if (sk->sk_type != SOCK_SEQPACKET)
|
||||
return;
|
||||
|
||||
if (encrypt == 0x00) {
|
||||
if (l2cap_pi(sk)->sec_level == BT_SECURITY_MEDIUM) {
|
||||
l2cap_sock_clear_timer(sk);
|
||||
l2cap_sock_set_timer(sk, HZ * 5);
|
||||
} else if (l2cap_pi(sk)->sec_level == BT_SECURITY_HIGH)
|
||||
__l2cap_sock_close(sk, ECONNREFUSED);
|
||||
} else {
|
||||
if (l2cap_pi(sk)->sec_level == BT_SECURITY_MEDIUM)
|
||||
l2cap_sock_clear_timer(sk);
|
||||
}
|
||||
}
|
||||
|
||||
static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
|
||||
{
|
||||
struct l2cap_chan_list *l;
|
||||
struct l2cap_conn *conn = hcon->l2cap_data;
|
||||
@@ -2200,85 +2547,16 @@ static int l2cap_auth_cfm(struct hci_conn *hcon, u8 status)
|
||||
read_lock(&l->lock);
|
||||
|
||||
for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
|
||||
struct l2cap_pinfo *pi = l2cap_pi(sk);
|
||||
|
||||
bh_lock_sock(sk);
|
||||
|
||||
if ((pi->link_mode & (L2CAP_LM_ENCRYPT | L2CAP_LM_SECURE)) &&
|
||||
!(hcon->link_mode & HCI_LM_ENCRYPT) &&
|
||||
!status) {
|
||||
if (l2cap_pi(sk)->conf_state & L2CAP_CONF_CONNECT_PEND) {
|
||||
bh_unlock_sock(sk);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sk->sk_state == BT_CONNECT) {
|
||||
if (!status) {
|
||||
struct l2cap_conn_req req;
|
||||
req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
|
||||
req.psm = l2cap_pi(sk)->psm;
|
||||
|
||||
l2cap_pi(sk)->ident = l2cap_get_ident(conn);
|
||||
|
||||
l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
|
||||
L2CAP_CONN_REQ, sizeof(req), &req);
|
||||
} else {
|
||||
l2cap_sock_clear_timer(sk);
|
||||
l2cap_sock_set_timer(sk, HZ / 10);
|
||||
}
|
||||
} else if (sk->sk_state == BT_CONNECT2) {
|
||||
struct l2cap_conn_rsp rsp;
|
||||
__u16 result;
|
||||
|
||||
if (!status) {
|
||||
sk->sk_state = BT_CONFIG;
|
||||
result = L2CAP_CR_SUCCESS;
|
||||
} else {
|
||||
sk->sk_state = BT_DISCONN;
|
||||
l2cap_sock_set_timer(sk, HZ / 10);
|
||||
result = L2CAP_CR_SEC_BLOCK;
|
||||
}
|
||||
|
||||
rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid);
|
||||
rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid);
|
||||
rsp.result = cpu_to_le16(result);
|
||||
rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
|
||||
l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
|
||||
L2CAP_CONN_RSP, sizeof(rsp), &rsp);
|
||||
}
|
||||
|
||||
bh_unlock_sock(sk);
|
||||
}
|
||||
|
||||
read_unlock(&l->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l2cap_encrypt_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
|
||||
{
|
||||
struct l2cap_chan_list *l;
|
||||
struct l2cap_conn *conn = hcon->l2cap_data;
|
||||
struct sock *sk;
|
||||
|
||||
if (!conn)
|
||||
return 0;
|
||||
|
||||
l = &conn->chan_list;
|
||||
|
||||
BT_DBG("conn %p", conn);
|
||||
|
||||
read_lock(&l->lock);
|
||||
|
||||
for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
|
||||
struct l2cap_pinfo *pi = l2cap_pi(sk);
|
||||
|
||||
bh_lock_sock(sk);
|
||||
|
||||
if ((pi->link_mode & (L2CAP_LM_ENCRYPT | L2CAP_LM_SECURE)) &&
|
||||
(sk->sk_state == BT_CONNECTED ||
|
||||
sk->sk_state == BT_CONFIG) &&
|
||||
!status && encrypt == 0x00) {
|
||||
__l2cap_sock_close(sk, ECONNREFUSED);
|
||||
if (!status && (sk->sk_state == BT_CONNECTED ||
|
||||
sk->sk_state == BT_CONFIG)) {
|
||||
l2cap_check_encryption(sk, encrypt);
|
||||
bh_unlock_sock(sk);
|
||||
continue;
|
||||
}
|
||||
@@ -2376,7 +2654,7 @@ static int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 fl
|
||||
goto drop;
|
||||
|
||||
skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len),
|
||||
skb->len);
|
||||
skb->len);
|
||||
conn->rx_len = len - skb->len;
|
||||
} else {
|
||||
BT_DBG("Cont: frag len %d (expecting %d)", skb->len, conn->rx_len);
|
||||
@@ -2398,7 +2676,7 @@ static int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 fl
|
||||
}
|
||||
|
||||
skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len),
|
||||
skb->len);
|
||||
skb->len);
|
||||
conn->rx_len -= skb->len;
|
||||
|
||||
if (!conn->rx_len) {
|
||||
@@ -2424,10 +2702,10 @@ static ssize_t l2cap_sysfs_show(struct class *dev, char *buf)
|
||||
sk_for_each(sk, node, &l2cap_sk_list.head) {
|
||||
struct l2cap_pinfo *pi = l2cap_pi(sk);
|
||||
|
||||
str += sprintf(str, "%s %s %d %d 0x%4.4x 0x%4.4x %d %d 0x%x\n",
|
||||
str += sprintf(str, "%s %s %d %d 0x%4.4x 0x%4.4x %d %d %d\n",
|
||||
batostr(&bt_sk(sk)->src), batostr(&bt_sk(sk)->dst),
|
||||
sk->sk_state, btohs(pi->psm), pi->scid, pi->dcid,
|
||||
pi->imtu, pi->omtu, pi->link_mode);
|
||||
pi->imtu, pi->omtu, pi->sec_level);
|
||||
}
|
||||
|
||||
read_unlock_bh(&l2cap_sk_list.lock);
|
||||
@@ -2447,7 +2725,7 @@ static const struct proto_ops l2cap_sock_ops = {
|
||||
.accept = l2cap_sock_accept,
|
||||
.getname = l2cap_sock_getname,
|
||||
.sendmsg = l2cap_sock_sendmsg,
|
||||
.recvmsg = bt_sock_recvmsg,
|
||||
.recvmsg = l2cap_sock_recvmsg,
|
||||
.poll = bt_sock_poll,
|
||||
.ioctl = bt_sock_ioctl,
|
||||
.mmap = sock_no_mmap,
|
||||
@@ -2469,8 +2747,8 @@ static struct hci_proto l2cap_hci_proto = {
|
||||
.connect_ind = l2cap_connect_ind,
|
||||
.connect_cfm = l2cap_connect_cfm,
|
||||
.disconn_ind = l2cap_disconn_ind,
|
||||
.auth_cfm = l2cap_auth_cfm,
|
||||
.encrypt_cfm = l2cap_encrypt_cfm,
|
||||
.disconn_cfm = l2cap_disconn_cfm,
|
||||
.security_cfm = l2cap_security_cfm,
|
||||
.recv_acldata = l2cap_recv_acldata
|
||||
};
|
||||
|
||||
|
||||
+100
-79
@@ -46,7 +46,7 @@
|
||||
#include <net/bluetooth/l2cap.h>
|
||||
#include <net/bluetooth/rfcomm.h>
|
||||
|
||||
#define VERSION "1.10"
|
||||
#define VERSION "1.11"
|
||||
|
||||
static int disable_cfc = 0;
|
||||
static int channel_mtu = -1;
|
||||
@@ -223,19 +223,25 @@ static int rfcomm_l2sock_create(struct socket **sock)
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline int rfcomm_check_link_mode(struct rfcomm_dlc *d)
|
||||
static inline int rfcomm_check_security(struct rfcomm_dlc *d)
|
||||
{
|
||||
struct sock *sk = d->session->sock->sk;
|
||||
__u8 auth_type;
|
||||
|
||||
if (d->link_mode & (RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE)) {
|
||||
if (!hci_conn_encrypt(l2cap_pi(sk)->conn->hcon))
|
||||
return 1;
|
||||
} else if (d->link_mode & RFCOMM_LM_AUTH) {
|
||||
if (!hci_conn_auth(l2cap_pi(sk)->conn->hcon))
|
||||
return 1;
|
||||
switch (d->sec_level) {
|
||||
case BT_SECURITY_HIGH:
|
||||
auth_type = HCI_AT_GENERAL_BONDING_MITM;
|
||||
break;
|
||||
case BT_SECURITY_MEDIUM:
|
||||
auth_type = HCI_AT_GENERAL_BONDING;
|
||||
break;
|
||||
default:
|
||||
auth_type = HCI_AT_NO_BONDING;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return hci_conn_security(l2cap_pi(sk)->conn->hcon, d->sec_level,
|
||||
auth_type);
|
||||
}
|
||||
|
||||
/* ---- RFCOMM DLCs ---- */
|
||||
@@ -388,10 +394,10 @@ static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst,
|
||||
d->cfc = (s->cfc == RFCOMM_CFC_UNKNOWN) ? 0 : s->cfc;
|
||||
|
||||
if (s->state == BT_CONNECTED) {
|
||||
if (rfcomm_check_link_mode(d))
|
||||
set_bit(RFCOMM_AUTH_PENDING, &d->flags);
|
||||
else
|
||||
if (rfcomm_check_security(d))
|
||||
rfcomm_send_pn(s, 1, d);
|
||||
else
|
||||
set_bit(RFCOMM_AUTH_PENDING, &d->flags);
|
||||
}
|
||||
|
||||
rfcomm_dlc_set_timer(d, RFCOMM_CONN_TIMEOUT);
|
||||
@@ -421,9 +427,16 @@ static int __rfcomm_dlc_close(struct rfcomm_dlc *d, int err)
|
||||
d, d->state, d->dlci, err, s);
|
||||
|
||||
switch (d->state) {
|
||||
case BT_CONNECTED:
|
||||
case BT_CONFIG:
|
||||
case BT_CONNECT:
|
||||
case BT_CONFIG:
|
||||
if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) {
|
||||
set_bit(RFCOMM_AUTH_REJECT, &d->flags);
|
||||
rfcomm_schedule(RFCOMM_SCHED_AUTH);
|
||||
break;
|
||||
}
|
||||
/* Fall through */
|
||||
|
||||
case BT_CONNECTED:
|
||||
d->state = BT_DISCONN;
|
||||
if (skb_queue_empty(&d->tx_queue)) {
|
||||
rfcomm_send_disc(s, d->dlci);
|
||||
@@ -434,6 +447,15 @@ static int __rfcomm_dlc_close(struct rfcomm_dlc *d, int err)
|
||||
}
|
||||
break;
|
||||
|
||||
case BT_OPEN:
|
||||
case BT_CONNECT2:
|
||||
if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) {
|
||||
set_bit(RFCOMM_AUTH_REJECT, &d->flags);
|
||||
rfcomm_schedule(RFCOMM_SCHED_AUTH);
|
||||
break;
|
||||
}
|
||||
/* Fall through */
|
||||
|
||||
default:
|
||||
rfcomm_dlc_clear_timer(d);
|
||||
|
||||
@@ -636,6 +658,7 @@ static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, bdaddr_t *dst
|
||||
bacpy(&addr.l2_bdaddr, src);
|
||||
addr.l2_family = AF_BLUETOOTH;
|
||||
addr.l2_psm = 0;
|
||||
addr.l2_cid = 0;
|
||||
*err = kernel_bind(sock, (struct sockaddr *) &addr, sizeof(addr));
|
||||
if (*err < 0)
|
||||
goto failed;
|
||||
@@ -657,6 +680,7 @@ static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, bdaddr_t *dst
|
||||
bacpy(&addr.l2_bdaddr, dst);
|
||||
addr.l2_family = AF_BLUETOOTH;
|
||||
addr.l2_psm = htobs(RFCOMM_PSM);
|
||||
addr.l2_cid = 0;
|
||||
*err = kernel_connect(sock, (struct sockaddr *) &addr, sizeof(addr), O_NONBLOCK);
|
||||
if (*err == 0 || *err == -EINPROGRESS)
|
||||
return s;
|
||||
@@ -1162,7 +1186,7 @@ static int rfcomm_recv_disc(struct rfcomm_session *s, u8 dlci)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rfcomm_dlc_accept(struct rfcomm_dlc *d)
|
||||
void rfcomm_dlc_accept(struct rfcomm_dlc *d)
|
||||
{
|
||||
struct sock *sk = d->session->sock->sk;
|
||||
|
||||
@@ -1175,12 +1199,31 @@ static void rfcomm_dlc_accept(struct rfcomm_dlc *d)
|
||||
d->state_change(d, 0);
|
||||
rfcomm_dlc_unlock(d);
|
||||
|
||||
if (d->link_mode & RFCOMM_LM_MASTER)
|
||||
if (d->role_switch)
|
||||
hci_conn_switch_role(l2cap_pi(sk)->conn->hcon, 0x00);
|
||||
|
||||
rfcomm_send_msc(d->session, 1, d->dlci, d->v24_sig);
|
||||
}
|
||||
|
||||
static void rfcomm_check_accept(struct rfcomm_dlc *d)
|
||||
{
|
||||
if (rfcomm_check_security(d)) {
|
||||
if (d->defer_setup) {
|
||||
set_bit(RFCOMM_DEFER_SETUP, &d->flags);
|
||||
rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT);
|
||||
|
||||
rfcomm_dlc_lock(d);
|
||||
d->state = BT_CONNECT2;
|
||||
d->state_change(d, 0);
|
||||
rfcomm_dlc_unlock(d);
|
||||
} else
|
||||
rfcomm_dlc_accept(d);
|
||||
} else {
|
||||
set_bit(RFCOMM_AUTH_PENDING, &d->flags);
|
||||
rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
static int rfcomm_recv_sabm(struct rfcomm_session *s, u8 dlci)
|
||||
{
|
||||
struct rfcomm_dlc *d;
|
||||
@@ -1203,11 +1246,7 @@ static int rfcomm_recv_sabm(struct rfcomm_session *s, u8 dlci)
|
||||
if (d) {
|
||||
if (d->state == BT_OPEN) {
|
||||
/* DLC was previously opened by PN request */
|
||||
if (rfcomm_check_link_mode(d)) {
|
||||
set_bit(RFCOMM_AUTH_PENDING, &d->flags);
|
||||
rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT);
|
||||
} else
|
||||
rfcomm_dlc_accept(d);
|
||||
rfcomm_check_accept(d);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -1219,11 +1258,7 @@ static int rfcomm_recv_sabm(struct rfcomm_session *s, u8 dlci)
|
||||
d->addr = __addr(s->initiator, dlci);
|
||||
rfcomm_dlc_link(s, d);
|
||||
|
||||
if (rfcomm_check_link_mode(d)) {
|
||||
set_bit(RFCOMM_AUTH_PENDING, &d->flags);
|
||||
rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT);
|
||||
} else
|
||||
rfcomm_dlc_accept(d);
|
||||
rfcomm_check_accept(d);
|
||||
} else {
|
||||
rfcomm_send_dm(s, dlci);
|
||||
}
|
||||
@@ -1637,11 +1672,12 @@ static void rfcomm_process_connect(struct rfcomm_session *s)
|
||||
d = list_entry(p, struct rfcomm_dlc, list);
|
||||
if (d->state == BT_CONFIG) {
|
||||
d->mtu = s->mtu;
|
||||
if (rfcomm_check_link_mode(d)) {
|
||||
if (rfcomm_check_security(d)) {
|
||||
rfcomm_send_pn(s, 1, d);
|
||||
} else {
|
||||
set_bit(RFCOMM_AUTH_PENDING, &d->flags);
|
||||
rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT);
|
||||
} else
|
||||
rfcomm_send_pn(s, 1, d);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1717,11 +1753,17 @@ static inline void rfcomm_process_dlcs(struct rfcomm_session *s)
|
||||
if (d->out) {
|
||||
rfcomm_send_pn(s, 1, d);
|
||||
rfcomm_dlc_set_timer(d, RFCOMM_CONN_TIMEOUT);
|
||||
} else
|
||||
rfcomm_dlc_accept(d);
|
||||
if (d->link_mode & RFCOMM_LM_SECURE) {
|
||||
struct sock *sk = s->sock->sk;
|
||||
hci_conn_change_link_key(l2cap_pi(sk)->conn->hcon);
|
||||
} else {
|
||||
if (d->defer_setup) {
|
||||
set_bit(RFCOMM_DEFER_SETUP, &d->flags);
|
||||
rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT);
|
||||
|
||||
rfcomm_dlc_lock(d);
|
||||
d->state = BT_CONNECT2;
|
||||
d->state_change(d, 0);
|
||||
rfcomm_dlc_unlock(d);
|
||||
} else
|
||||
rfcomm_dlc_accept(d);
|
||||
}
|
||||
continue;
|
||||
} else if (test_and_clear_bit(RFCOMM_AUTH_REJECT, &d->flags)) {
|
||||
@@ -1734,6 +1776,9 @@ static inline void rfcomm_process_dlcs(struct rfcomm_session *s)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (test_bit(RFCOMM_SEC_PENDING, &d->flags))
|
||||
continue;
|
||||
|
||||
if (test_bit(RFCOMM_TX_THROTTLED, &s->flags))
|
||||
continue;
|
||||
|
||||
@@ -1876,6 +1921,7 @@ static int rfcomm_add_listener(bdaddr_t *ba)
|
||||
bacpy(&addr.l2_bdaddr, ba);
|
||||
addr.l2_family = AF_BLUETOOTH;
|
||||
addr.l2_psm = htobs(RFCOMM_PSM);
|
||||
addr.l2_cid = 0;
|
||||
err = kernel_bind(sock, (struct sockaddr *) &addr, sizeof(addr));
|
||||
if (err < 0) {
|
||||
BT_ERR("Bind failed %d", err);
|
||||
@@ -1947,42 +1993,7 @@ static int rfcomm_run(void *unused)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rfcomm_auth_cfm(struct hci_conn *conn, u8 status)
|
||||
{
|
||||
struct rfcomm_session *s;
|
||||
struct rfcomm_dlc *d;
|
||||
struct list_head *p, *n;
|
||||
|
||||
BT_DBG("conn %p status 0x%02x", conn, status);
|
||||
|
||||
s = rfcomm_session_get(&conn->hdev->bdaddr, &conn->dst);
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
rfcomm_session_hold(s);
|
||||
|
||||
list_for_each_safe(p, n, &s->dlcs) {
|
||||
d = list_entry(p, struct rfcomm_dlc, list);
|
||||
|
||||
if ((d->link_mode & (RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE)) &&
|
||||
!(conn->link_mode & HCI_LM_ENCRYPT) && !status)
|
||||
continue;
|
||||
|
||||
if (!test_and_clear_bit(RFCOMM_AUTH_PENDING, &d->flags))
|
||||
continue;
|
||||
|
||||
if (!status)
|
||||
set_bit(RFCOMM_AUTH_ACCEPT, &d->flags);
|
||||
else
|
||||
set_bit(RFCOMM_AUTH_REJECT, &d->flags);
|
||||
}
|
||||
|
||||
rfcomm_session_put(s);
|
||||
|
||||
rfcomm_schedule(RFCOMM_SCHED_AUTH);
|
||||
}
|
||||
|
||||
static void rfcomm_encrypt_cfm(struct hci_conn *conn, u8 status, u8 encrypt)
|
||||
static void rfcomm_security_cfm(struct hci_conn *conn, u8 status, u8 encrypt)
|
||||
{
|
||||
struct rfcomm_session *s;
|
||||
struct rfcomm_dlc *d;
|
||||
@@ -1999,18 +2010,29 @@ static void rfcomm_encrypt_cfm(struct hci_conn *conn, u8 status, u8 encrypt)
|
||||
list_for_each_safe(p, n, &s->dlcs) {
|
||||
d = list_entry(p, struct rfcomm_dlc, list);
|
||||
|
||||
if ((d->link_mode & (RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE)) &&
|
||||
(d->state == BT_CONNECTED ||
|
||||
d->state == BT_CONFIG) &&
|
||||
!status && encrypt == 0x00) {
|
||||
__rfcomm_dlc_close(d, ECONNREFUSED);
|
||||
continue;
|
||||
if (test_and_clear_bit(RFCOMM_SEC_PENDING, &d->flags)) {
|
||||
rfcomm_dlc_clear_timer(d);
|
||||
if (status || encrypt == 0x00) {
|
||||
__rfcomm_dlc_close(d, ECONNREFUSED);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (d->state == BT_CONNECTED && !status && encrypt == 0x00) {
|
||||
if (d->sec_level == BT_SECURITY_MEDIUM) {
|
||||
set_bit(RFCOMM_SEC_PENDING, &d->flags);
|
||||
rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT);
|
||||
continue;
|
||||
} else if (d->sec_level == BT_SECURITY_HIGH) {
|
||||
__rfcomm_dlc_close(d, ECONNREFUSED);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!test_and_clear_bit(RFCOMM_AUTH_PENDING, &d->flags))
|
||||
continue;
|
||||
|
||||
if (!status && encrypt)
|
||||
if (!status)
|
||||
set_bit(RFCOMM_AUTH_ACCEPT, &d->flags);
|
||||
else
|
||||
set_bit(RFCOMM_AUTH_REJECT, &d->flags);
|
||||
@@ -2023,8 +2045,7 @@ static void rfcomm_encrypt_cfm(struct hci_conn *conn, u8 status, u8 encrypt)
|
||||
|
||||
static struct hci_cb rfcomm_cb = {
|
||||
.name = "RFCOMM",
|
||||
.auth_cfm = rfcomm_auth_cfm,
|
||||
.encrypt_cfm = rfcomm_encrypt_cfm
|
||||
.security_cfm = rfcomm_security_cfm
|
||||
};
|
||||
|
||||
static ssize_t rfcomm_dlc_sysfs_show(struct class *dev, char *buf)
|
||||
|
||||
+191
-22
@@ -261,12 +261,19 @@ static void rfcomm_sock_init(struct sock *sk, struct sock *parent)
|
||||
|
||||
if (parent) {
|
||||
sk->sk_type = parent->sk_type;
|
||||
pi->link_mode = rfcomm_pi(parent)->link_mode;
|
||||
pi->dlc->defer_setup = bt_sk(parent)->defer_setup;
|
||||
|
||||
pi->sec_level = rfcomm_pi(parent)->sec_level;
|
||||
pi->role_switch = rfcomm_pi(parent)->role_switch;
|
||||
} else {
|
||||
pi->link_mode = 0;
|
||||
pi->dlc->defer_setup = 0;
|
||||
|
||||
pi->sec_level = BT_SECURITY_LOW;
|
||||
pi->role_switch = 0;
|
||||
}
|
||||
|
||||
pi->dlc->link_mode = pi->link_mode;
|
||||
pi->dlc->sec_level = pi->sec_level;
|
||||
pi->dlc->role_switch = pi->role_switch;
|
||||
}
|
||||
|
||||
static struct proto rfcomm_proto = {
|
||||
@@ -406,7 +413,8 @@ static int rfcomm_sock_connect(struct socket *sock, struct sockaddr *addr, int a
|
||||
bacpy(&bt_sk(sk)->dst, &sa->rc_bdaddr);
|
||||
rfcomm_pi(sk)->channel = sa->rc_channel;
|
||||
|
||||
d->link_mode = rfcomm_pi(sk)->link_mode;
|
||||
d->sec_level = rfcomm_pi(sk)->sec_level;
|
||||
d->role_switch = rfcomm_pi(sk)->role_switch;
|
||||
|
||||
err = rfcomm_dlc_open(d, &bt_sk(sk)->src, &sa->rc_bdaddr, sa->rc_channel);
|
||||
if (!err)
|
||||
@@ -554,6 +562,9 @@ static int rfcomm_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
|
||||
struct sk_buff *skb;
|
||||
int sent = 0;
|
||||
|
||||
if (test_bit(RFCOMM_DEFER_SETUP, &d->flags))
|
||||
return -ENOTCONN;
|
||||
|
||||
if (msg->msg_flags & MSG_OOB)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
@@ -570,8 +581,11 @@ static int rfcomm_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
|
||||
|
||||
skb = sock_alloc_send_skb(sk, size + RFCOMM_SKB_RESERVE,
|
||||
msg->msg_flags & MSG_DONTWAIT, &err);
|
||||
if (!skb)
|
||||
if (!skb) {
|
||||
if (sent == 0)
|
||||
sent = err;
|
||||
break;
|
||||
}
|
||||
skb_reserve(skb, RFCOMM_SKB_HEAD_RESERVE);
|
||||
|
||||
err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);
|
||||
@@ -630,10 +644,16 @@ static int rfcomm_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
|
||||
struct msghdr *msg, size_t size, int flags)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc;
|
||||
int err = 0;
|
||||
size_t target, copied = 0;
|
||||
long timeo;
|
||||
|
||||
if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) {
|
||||
rfcomm_dlc_accept(d);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (flags & MSG_OOB)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
@@ -710,7 +730,7 @@ out:
|
||||
return copied ? : err;
|
||||
}
|
||||
|
||||
static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, int optlen)
|
||||
static int rfcomm_sock_setsockopt_old(struct socket *sock, int optname, char __user *optval, int optlen)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
int err = 0;
|
||||
@@ -727,7 +747,144 @@ static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, c
|
||||
break;
|
||||
}
|
||||
|
||||
rfcomm_pi(sk)->link_mode = opt;
|
||||
if (opt & RFCOMM_LM_AUTH)
|
||||
rfcomm_pi(sk)->sec_level = BT_SECURITY_LOW;
|
||||
if (opt & RFCOMM_LM_ENCRYPT)
|
||||
rfcomm_pi(sk)->sec_level = BT_SECURITY_MEDIUM;
|
||||
if (opt & RFCOMM_LM_SECURE)
|
||||
rfcomm_pi(sk)->sec_level = BT_SECURITY_HIGH;
|
||||
|
||||
rfcomm_pi(sk)->role_switch = (opt & RFCOMM_LM_MASTER);
|
||||
break;
|
||||
|
||||
default:
|
||||
err = -ENOPROTOOPT;
|
||||
break;
|
||||
}
|
||||
|
||||
release_sock(sk);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, int optlen)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
struct bt_security sec;
|
||||
int len, err = 0;
|
||||
u32 opt;
|
||||
|
||||
BT_DBG("sk %p", sk);
|
||||
|
||||
if (level == SOL_RFCOMM)
|
||||
return rfcomm_sock_setsockopt_old(sock, optname, optval, optlen);
|
||||
|
||||
if (level != SOL_BLUETOOTH)
|
||||
return -ENOPROTOOPT;
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
switch (optname) {
|
||||
case BT_SECURITY:
|
||||
if (sk->sk_type != SOCK_STREAM) {
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
sec.level = BT_SECURITY_LOW;
|
||||
|
||||
len = min_t(unsigned int, sizeof(sec), optlen);
|
||||
if (copy_from_user((char *) &sec, optval, len)) {
|
||||
err = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
if (sec.level > BT_SECURITY_HIGH) {
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
rfcomm_pi(sk)->sec_level = sec.level;
|
||||
break;
|
||||
|
||||
case BT_DEFER_SETUP:
|
||||
if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) {
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (get_user(opt, (u32 __user *) optval)) {
|
||||
err = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
bt_sk(sk)->defer_setup = opt;
|
||||
break;
|
||||
|
||||
default:
|
||||
err = -ENOPROTOOPT;
|
||||
break;
|
||||
}
|
||||
|
||||
release_sock(sk);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __user *optval, int __user *optlen)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
struct sock *l2cap_sk;
|
||||
struct rfcomm_conninfo cinfo;
|
||||
int len, err = 0;
|
||||
u32 opt;
|
||||
|
||||
BT_DBG("sk %p", sk);
|
||||
|
||||
if (get_user(len, optlen))
|
||||
return -EFAULT;
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
switch (optname) {
|
||||
case RFCOMM_LM:
|
||||
switch (rfcomm_pi(sk)->sec_level) {
|
||||
case BT_SECURITY_LOW:
|
||||
opt = RFCOMM_LM_AUTH;
|
||||
break;
|
||||
case BT_SECURITY_MEDIUM:
|
||||
opt = RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT;
|
||||
break;
|
||||
case BT_SECURITY_HIGH:
|
||||
opt = RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT |
|
||||
RFCOMM_LM_SECURE;
|
||||
break;
|
||||
default:
|
||||
opt = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (rfcomm_pi(sk)->role_switch)
|
||||
opt |= RFCOMM_LM_MASTER;
|
||||
|
||||
if (put_user(opt, (u32 __user *) optval))
|
||||
err = -EFAULT;
|
||||
break;
|
||||
|
||||
case RFCOMM_CONNINFO:
|
||||
if (sk->sk_state != BT_CONNECTED &&
|
||||
!rfcomm_pi(sk)->dlc->defer_setup) {
|
||||
err = -ENOTCONN;
|
||||
break;
|
||||
}
|
||||
|
||||
l2cap_sk = rfcomm_pi(sk)->dlc->session->sock->sk;
|
||||
|
||||
cinfo.hci_handle = l2cap_pi(l2cap_sk)->conn->hcon->handle;
|
||||
memcpy(cinfo.dev_class, l2cap_pi(l2cap_sk)->conn->hcon->dev_class, 3);
|
||||
|
||||
len = min_t(unsigned int, len, sizeof(cinfo));
|
||||
if (copy_to_user(optval, (char *) &cinfo, len))
|
||||
err = -EFAULT;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -742,36 +899,44 @@ static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, c
|
||||
static int rfcomm_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
struct sock *l2cap_sk;
|
||||
struct rfcomm_conninfo cinfo;
|
||||
struct bt_security sec;
|
||||
int len, err = 0;
|
||||
|
||||
BT_DBG("sk %p", sk);
|
||||
|
||||
if (level == SOL_RFCOMM)
|
||||
return rfcomm_sock_getsockopt_old(sock, optname, optval, optlen);
|
||||
|
||||
if (level != SOL_BLUETOOTH)
|
||||
return -ENOPROTOOPT;
|
||||
|
||||
if (get_user(len, optlen))
|
||||
return -EFAULT;
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
switch (optname) {
|
||||
case RFCOMM_LM:
|
||||
if (put_user(rfcomm_pi(sk)->link_mode, (u32 __user *) optval))
|
||||
err = -EFAULT;
|
||||
break;
|
||||
|
||||
case RFCOMM_CONNINFO:
|
||||
if (sk->sk_state != BT_CONNECTED) {
|
||||
err = -ENOTCONN;
|
||||
case BT_SECURITY:
|
||||
if (sk->sk_type != SOCK_STREAM) {
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
l2cap_sk = rfcomm_pi(sk)->dlc->session->sock->sk;
|
||||
sec.level = rfcomm_pi(sk)->sec_level;
|
||||
|
||||
cinfo.hci_handle = l2cap_pi(l2cap_sk)->conn->hcon->handle;
|
||||
memcpy(cinfo.dev_class, l2cap_pi(l2cap_sk)->conn->hcon->dev_class, 3);
|
||||
len = min_t(unsigned int, len, sizeof(sec));
|
||||
if (copy_to_user(optval, (char *) &sec, len))
|
||||
err = -EFAULT;
|
||||
|
||||
len = min_t(unsigned int, len, sizeof(cinfo));
|
||||
if (copy_to_user(optval, (char *) &cinfo, len))
|
||||
break;
|
||||
|
||||
case BT_DEFER_SETUP:
|
||||
if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) {
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (put_user(bt_sk(sk)->defer_setup, (u32 __user *) optval))
|
||||
err = -EFAULT;
|
||||
|
||||
break;
|
||||
@@ -888,6 +1053,10 @@ int rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, struct rfcomm_dlc *
|
||||
|
||||
done:
|
||||
bh_unlock_sock(parent);
|
||||
|
||||
if (bt_sk(parent)->defer_setup)
|
||||
parent->sk_state_change(parent);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -731,7 +731,8 @@ static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp)
|
||||
remove_wait_queue(&dev->wait, &wait);
|
||||
|
||||
if (err == 0)
|
||||
device_move(dev->tty_dev, rfcomm_get_device(dev));
|
||||
device_move(dev->tty_dev, rfcomm_get_device(dev),
|
||||
DPM_ORDER_DEV_AFTER_PARENT);
|
||||
|
||||
rfcomm_tty_copy_pending(dev);
|
||||
|
||||
@@ -751,7 +752,7 @@ static void rfcomm_tty_close(struct tty_struct *tty, struct file *filp)
|
||||
|
||||
if (atomic_dec_and_test(&dev->opened)) {
|
||||
if (dev->tty_dev->parent)
|
||||
device_move(dev->tty_dev, NULL);
|
||||
device_move(dev->tty_dev, NULL, DPM_ORDER_DEV_LAST);
|
||||
|
||||
/* Close DLC and dettach TTY */
|
||||
rfcomm_dlc_close(dev->dlc, 0);
|
||||
|
||||
+51
-6
@@ -195,7 +195,7 @@ static int sco_connect(struct sock *sk)
|
||||
else
|
||||
type = SCO_LINK;
|
||||
|
||||
hcon = hci_connect(hdev, type, dst, HCI_AT_NO_BONDING);
|
||||
hcon = hci_connect(hdev, type, dst, BT_SECURITY_LOW, HCI_AT_NO_BONDING);
|
||||
if (!hcon)
|
||||
goto done;
|
||||
|
||||
@@ -668,7 +668,7 @@ static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen)
|
||||
static int sco_sock_getsockopt_old(struct socket *sock, int optname, char __user *optval, int __user *optlen)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
struct sco_options opts;
|
||||
@@ -723,6 +723,31 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
int len, err = 0;
|
||||
|
||||
BT_DBG("sk %p", sk);
|
||||
|
||||
if (level == SOL_SCO)
|
||||
return sco_sock_getsockopt_old(sock, optname, optval, optlen);
|
||||
|
||||
if (get_user(len, optlen))
|
||||
return -EFAULT;
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
switch (optname) {
|
||||
default:
|
||||
err = -ENOPROTOOPT;
|
||||
break;
|
||||
}
|
||||
|
||||
release_sock(sk);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sco_sock_release(struct socket *sock)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
@@ -832,10 +857,30 @@ done:
|
||||
/* ----- SCO interface with lower layer (HCI) ----- */
|
||||
static int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type)
|
||||
{
|
||||
register struct sock *sk;
|
||||
struct hlist_node *node;
|
||||
int lm = 0;
|
||||
|
||||
if (type != SCO_LINK && type != ESCO_LINK)
|
||||
return 0;
|
||||
|
||||
BT_DBG("hdev %s, bdaddr %s", hdev->name, batostr(bdaddr));
|
||||
|
||||
/* Always accept connection */
|
||||
return HCI_LM_ACCEPT;
|
||||
/* Find listening sockets */
|
||||
read_lock(&sco_sk_list.lock);
|
||||
sk_for_each(sk, node, &sco_sk_list.head) {
|
||||
if (sk->sk_state != BT_LISTEN)
|
||||
continue;
|
||||
|
||||
if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr) ||
|
||||
!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) {
|
||||
lm |= HCI_LM_ACCEPT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
read_unlock(&sco_sk_list.lock);
|
||||
|
||||
return lm;
|
||||
}
|
||||
|
||||
static int sco_connect_cfm(struct hci_conn *hcon, __u8 status)
|
||||
@@ -857,7 +902,7 @@ static int sco_connect_cfm(struct hci_conn *hcon, __u8 status)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sco_disconn_ind(struct hci_conn *hcon, __u8 reason)
|
||||
static int sco_disconn_cfm(struct hci_conn *hcon, __u8 reason)
|
||||
{
|
||||
BT_DBG("hcon %p reason %d", hcon, reason);
|
||||
|
||||
@@ -940,7 +985,7 @@ static struct hci_proto sco_hci_proto = {
|
||||
.id = HCI_PROTO_SCO,
|
||||
.connect_ind = sco_connect_ind,
|
||||
.connect_cfm = sco_connect_cfm,
|
||||
.disconn_ind = sco_disconn_ind,
|
||||
.disconn_cfm = sco_disconn_cfm,
|
||||
.recv_scodata = sco_recv_scodata
|
||||
};
|
||||
|
||||
|
||||
@@ -426,7 +426,6 @@ err2:
|
||||
err1:
|
||||
kobject_del(&p->kobj);
|
||||
err0:
|
||||
kobject_put(&p->kobj);
|
||||
dev_set_promiscuity(dev, -1);
|
||||
put_back:
|
||||
dev_put(dev);
|
||||
|
||||
@@ -107,7 +107,7 @@ static void fake_update_pmtu(struct dst_entry *dst, u32 mtu)
|
||||
|
||||
static struct dst_ops fake_dst_ops = {
|
||||
.family = AF_INET,
|
||||
.protocol = __constant_htons(ETH_P_IP),
|
||||
.protocol = cpu_to_be16(ETH_P_IP),
|
||||
.update_pmtu = fake_update_pmtu,
|
||||
.entries = ATOMIC_INIT(0),
|
||||
};
|
||||
|
||||
@@ -98,7 +98,8 @@ void br_ifinfo_notify(int event, struct net_bridge_port *port)
|
||||
kfree_skb(skb);
|
||||
goto errout;
|
||||
}
|
||||
err = rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC);
|
||||
rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC);
|
||||
return;
|
||||
errout:
|
||||
if (err < 0)
|
||||
rtnl_set_sk_err(net, RTNLGRP_LINK, err);
|
||||
|
||||
@@ -214,7 +214,7 @@ static struct xt_target ebt_log_tg_reg __read_mostly = {
|
||||
.me = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const struct nf_logger ebt_log_logger = {
|
||||
static struct nf_logger ebt_log_logger __read_mostly = {
|
||||
.name = "ebt_log",
|
||||
.logfn = &ebt_log_packet,
|
||||
.me = THIS_MODULE,
|
||||
|
||||
@@ -279,21 +279,21 @@ static struct xt_target ebt_ulog_tg_reg __read_mostly = {
|
||||
.me = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const struct nf_logger ebt_ulog_logger = {
|
||||
.name = "ulog",
|
||||
static struct nf_logger ebt_ulog_logger __read_mostly = {
|
||||
.name = "ebt_ulog",
|
||||
.logfn = &ebt_log_packet,
|
||||
.me = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int __init ebt_ulog_init(void)
|
||||
{
|
||||
bool ret = true;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
if (nlbufsiz >= 128*1024) {
|
||||
printk(KERN_NOTICE "ebt_ulog: Netlink buffer has to be <= 128kB,"
|
||||
" please try a smaller nlbufsiz parameter.\n");
|
||||
return false;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* initialize ulog_buffers */
|
||||
@@ -308,12 +308,12 @@ static int __init ebt_ulog_init(void)
|
||||
if (!ebtulognl) {
|
||||
printk(KERN_WARNING KBUILD_MODNAME ": out of memory trying to "
|
||||
"call netlink_kernel_create\n");
|
||||
ret = false;
|
||||
} else if (xt_register_target(&ebt_ulog_tg_reg) != 0) {
|
||||
ret = -ENOMEM;
|
||||
} else if ((ret = xt_register_target(&ebt_ulog_tg_reg)) != 0) {
|
||||
netlink_kernel_release(ebtulognl);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
if (ret == 0)
|
||||
nf_log_register(NFPROTO_BRIDGE, &ebt_ulog_logger);
|
||||
|
||||
return ret;
|
||||
|
||||
@@ -46,7 +46,6 @@ static struct ebt_table broute_table =
|
||||
.name = "broute",
|
||||
.table = &initial_table,
|
||||
.valid_hooks = 1 << NF_BR_BROUTING,
|
||||
.lock = __RW_LOCK_UNLOCKED(broute_table.lock),
|
||||
.check = check,
|
||||
.me = THIS_MODULE,
|
||||
};
|
||||
|
||||
@@ -55,7 +55,6 @@ static struct ebt_table frame_filter =
|
||||
.name = "filter",
|
||||
.table = &initial_table,
|
||||
.valid_hooks = FILTER_VALID_HOOKS,
|
||||
.lock = __RW_LOCK_UNLOCKED(frame_filter.lock),
|
||||
.check = check,
|
||||
.me = THIS_MODULE,
|
||||
};
|
||||
|
||||
@@ -55,7 +55,6 @@ static struct ebt_table frame_nat =
|
||||
.name = "nat",
|
||||
.table = &initial_table,
|
||||
.valid_hooks = NAT_VALID_HOOKS,
|
||||
.lock = __RW_LOCK_UNLOCKED(frame_nat.lock),
|
||||
.check = check,
|
||||
.me = THIS_MODULE,
|
||||
};
|
||||
|
||||
+2
-3
@@ -273,8 +273,7 @@ int can_send(struct sk_buff *skb, int loop)
|
||||
err = net_xmit_errno(err);
|
||||
|
||||
if (err) {
|
||||
if (newskb)
|
||||
kfree_skb(newskb);
|
||||
kfree_skb(newskb);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -828,7 +827,7 @@ static int can_notifier(struct notifier_block *nb, unsigned long msg,
|
||||
*/
|
||||
|
||||
static struct packet_type can_packet __read_mostly = {
|
||||
.type = __constant_htons(ETH_P_CAN),
|
||||
.type = cpu_to_be16(ETH_P_CAN),
|
||||
.dev = NULL,
|
||||
.func = can_rcv,
|
||||
};
|
||||
|
||||
@@ -646,6 +646,9 @@ static int raw_sendmsg(struct kiocb *iocb, struct socket *sock,
|
||||
goto put_dev;
|
||||
|
||||
err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);
|
||||
if (err < 0)
|
||||
goto free_skb;
|
||||
err = sock_tx_timestamp(msg, sk, skb_tx(skb));
|
||||
if (err < 0)
|
||||
goto free_skb;
|
||||
skb->dev = dev;
|
||||
|
||||
+12
-7
@@ -216,7 +216,7 @@ Efault:
|
||||
int put_cmsg_compat(struct msghdr *kmsg, int level, int type, int len, void *data)
|
||||
{
|
||||
struct compat_timeval ctv;
|
||||
struct compat_timespec cts;
|
||||
struct compat_timespec cts[3];
|
||||
struct compat_cmsghdr __user *cm = (struct compat_cmsghdr __user *) kmsg->msg_control;
|
||||
struct compat_cmsghdr cmhdr;
|
||||
int cmlen;
|
||||
@@ -233,12 +233,17 @@ int put_cmsg_compat(struct msghdr *kmsg, int level, int type, int len, void *dat
|
||||
data = &ctv;
|
||||
len = sizeof(ctv);
|
||||
}
|
||||
if (level == SOL_SOCKET && type == SCM_TIMESTAMPNS) {
|
||||
if (level == SOL_SOCKET &&
|
||||
(type == SCM_TIMESTAMPNS || type == SCM_TIMESTAMPING)) {
|
||||
int count = type == SCM_TIMESTAMPNS ? 1 : 3;
|
||||
int i;
|
||||
struct timespec *ts = (struct timespec *)data;
|
||||
cts.tv_sec = ts->tv_sec;
|
||||
cts.tv_nsec = ts->tv_nsec;
|
||||
for (i = 0; i < count; i++) {
|
||||
cts[i].tv_sec = ts[i].tv_sec;
|
||||
cts[i].tv_nsec = ts[i].tv_nsec;
|
||||
}
|
||||
data = &cts;
|
||||
len = sizeof(cts);
|
||||
len = sizeof(cts[0]) * count;
|
||||
}
|
||||
|
||||
cmlen = CMSG_COMPAT_LEN(len);
|
||||
@@ -455,7 +460,7 @@ int compat_sock_get_timestamp(struct sock *sk, struct timeval __user *userstamp)
|
||||
struct timeval tv;
|
||||
|
||||
if (!sock_flag(sk, SOCK_TIMESTAMP))
|
||||
sock_enable_timestamp(sk);
|
||||
sock_enable_timestamp(sk, SOCK_TIMESTAMP);
|
||||
tv = ktime_to_timeval(sk->sk_stamp);
|
||||
if (tv.tv_sec == -1)
|
||||
return err;
|
||||
@@ -479,7 +484,7 @@ int compat_sock_get_timestampns(struct sock *sk, struct timespec __user *usersta
|
||||
struct timespec ts;
|
||||
|
||||
if (!sock_flag(sk, SOCK_TIMESTAMP))
|
||||
sock_enable_timestamp(sk);
|
||||
sock_enable_timestamp(sk, SOCK_TIMESTAMP);
|
||||
ts = ktime_to_timespec(sk->sk_stamp);
|
||||
if (ts.tv_sec == -1)
|
||||
return err;
|
||||
|
||||
@@ -17,3 +17,6 @@ obj-$(CONFIG_NET_PKTGEN) += pktgen.o
|
||||
obj-$(CONFIG_NETPOLL) += netpoll.o
|
||||
obj-$(CONFIG_NET_DMA) += user_dma.o
|
||||
obj-$(CONFIG_FIB_RULES) += fib_rules.o
|
||||
obj-$(CONFIG_TRACEPOINTS) += net-traces.o
|
||||
obj-$(CONFIG_NET_DROP_MONITOR) += drop_monitor.o
|
||||
|
||||
|
||||
+1
-1
@@ -208,7 +208,7 @@ struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags,
|
||||
|
||||
void skb_free_datagram(struct sock *sk, struct sk_buff *skb)
|
||||
{
|
||||
kfree_skb(skb);
|
||||
consume_skb(skb);
|
||||
sk_mem_reclaim_partial(sk);
|
||||
}
|
||||
|
||||
|
||||
+163
-111
@@ -1457,7 +1457,9 @@ static bool can_checksum_protocol(unsigned long features, __be16 protocol)
|
||||
((features & NETIF_F_IP_CSUM) &&
|
||||
protocol == htons(ETH_P_IP)) ||
|
||||
((features & NETIF_F_IPV6_CSUM) &&
|
||||
protocol == htons(ETH_P_IPV6)));
|
||||
protocol == htons(ETH_P_IPV6)) ||
|
||||
((features & NETIF_F_FCOE_CRC) &&
|
||||
protocol == htons(ETH_P_FCOE)));
|
||||
}
|
||||
|
||||
static bool dev_can_checksum(struct net_device *dev, struct sk_buff *skb)
|
||||
@@ -1668,8 +1670,8 @@ int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
|
||||
struct netdev_queue *txq)
|
||||
{
|
||||
const struct net_device_ops *ops = dev->netdev_ops;
|
||||
int rc;
|
||||
|
||||
prefetch(&dev->netdev_ops->ndo_start_xmit);
|
||||
if (likely(!skb->next)) {
|
||||
if (!list_empty(&ptype_all))
|
||||
dev_queue_xmit_nit(skb, dev);
|
||||
@@ -1681,13 +1683,27 @@ int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
|
||||
goto gso;
|
||||
}
|
||||
|
||||
return ops->ndo_start_xmit(skb, dev);
|
||||
rc = ops->ndo_start_xmit(skb, dev);
|
||||
/*
|
||||
* TODO: if skb_orphan() was called by
|
||||
* dev->hard_start_xmit() (for example, the unmodified
|
||||
* igb driver does that; bnx2 doesn't), then
|
||||
* skb_tx_software_timestamp() will be unable to send
|
||||
* back the time stamp.
|
||||
*
|
||||
* How can this be prevented? Always create another
|
||||
* reference to the socket before calling
|
||||
* dev->hard_start_xmit()? Prevent that skb_orphan()
|
||||
* does anything in dev->hard_start_xmit() by clearing
|
||||
* the skb destructor before the call and restoring it
|
||||
* afterwards, then doing the skb_orphan() ourselves?
|
||||
*/
|
||||
return rc;
|
||||
}
|
||||
|
||||
gso:
|
||||
do {
|
||||
struct sk_buff *nskb = skb->next;
|
||||
int rc;
|
||||
|
||||
skb->next = nskb->next;
|
||||
nskb->next = NULL;
|
||||
@@ -1708,59 +1724,24 @@ out_kfree_skb:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 simple_tx_hashrnd;
|
||||
static int simple_tx_hashrnd_initialized = 0;
|
||||
static u32 skb_tx_hashrnd;
|
||||
|
||||
static u16 simple_tx_hash(struct net_device *dev, struct sk_buff *skb)
|
||||
u16 skb_tx_hash(const struct net_device *dev, const struct sk_buff *skb)
|
||||
{
|
||||
u32 addr1, addr2, ports;
|
||||
u32 hash, ihl;
|
||||
u8 ip_proto = 0;
|
||||
u32 hash;
|
||||
|
||||
if (unlikely(!simple_tx_hashrnd_initialized)) {
|
||||
get_random_bytes(&simple_tx_hashrnd, 4);
|
||||
simple_tx_hashrnd_initialized = 1;
|
||||
}
|
||||
if (skb_rx_queue_recorded(skb)) {
|
||||
hash = skb_get_rx_queue(skb);
|
||||
} else if (skb->sk && skb->sk->sk_hash) {
|
||||
hash = skb->sk->sk_hash;
|
||||
} else
|
||||
hash = skb->protocol;
|
||||
|
||||
switch (skb->protocol) {
|
||||
case htons(ETH_P_IP):
|
||||
if (!(ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET)))
|
||||
ip_proto = ip_hdr(skb)->protocol;
|
||||
addr1 = ip_hdr(skb)->saddr;
|
||||
addr2 = ip_hdr(skb)->daddr;
|
||||
ihl = ip_hdr(skb)->ihl;
|
||||
break;
|
||||
case htons(ETH_P_IPV6):
|
||||
ip_proto = ipv6_hdr(skb)->nexthdr;
|
||||
addr1 = ipv6_hdr(skb)->saddr.s6_addr32[3];
|
||||
addr2 = ipv6_hdr(skb)->daddr.s6_addr32[3];
|
||||
ihl = (40 >> 2);
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
switch (ip_proto) {
|
||||
case IPPROTO_TCP:
|
||||
case IPPROTO_UDP:
|
||||
case IPPROTO_DCCP:
|
||||
case IPPROTO_ESP:
|
||||
case IPPROTO_AH:
|
||||
case IPPROTO_SCTP:
|
||||
case IPPROTO_UDPLITE:
|
||||
ports = *((u32 *) (skb_network_header(skb) + (ihl * 4)));
|
||||
break;
|
||||
|
||||
default:
|
||||
ports = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
hash = jhash_3words(addr1, addr2, ports, simple_tx_hashrnd);
|
||||
hash = jhash_1word(hash, skb_tx_hashrnd);
|
||||
|
||||
return (u16) (((u64) hash * dev->real_num_tx_queues) >> 32);
|
||||
}
|
||||
EXPORT_SYMBOL(skb_tx_hash);
|
||||
|
||||
static struct netdev_queue *dev_pick_tx(struct net_device *dev,
|
||||
struct sk_buff *skb)
|
||||
@@ -1771,7 +1752,7 @@ static struct netdev_queue *dev_pick_tx(struct net_device *dev,
|
||||
if (ops->ndo_select_queue)
|
||||
queue_index = ops->ndo_select_queue(dev, skb);
|
||||
else if (dev->real_num_tx_queues > 1)
|
||||
queue_index = simple_tx_hash(dev, skb);
|
||||
queue_index = skb_tx_hash(dev, skb);
|
||||
|
||||
skb_set_queue_mapping(skb, queue_index);
|
||||
return netdev_get_tx_queue(dev, queue_index);
|
||||
@@ -2297,6 +2278,8 @@ ncls:
|
||||
if (!skb)
|
||||
goto out;
|
||||
|
||||
skb_orphan(skb);
|
||||
|
||||
type = skb->protocol;
|
||||
list_for_each_entry_rcu(ptype,
|
||||
&ptype_base[ntohs(type) & PTYPE_HASH_MASK], list) {
|
||||
@@ -2366,7 +2349,6 @@ static int napi_gro_complete(struct sk_buff *skb)
|
||||
|
||||
out:
|
||||
skb_shinfo(skb)->gso_size = 0;
|
||||
__skb_push(skb, -skb_network_offset(skb));
|
||||
return netif_receive_skb(skb);
|
||||
}
|
||||
|
||||
@@ -2380,20 +2362,40 @@ void napi_gro_flush(struct napi_struct *napi)
|
||||
napi_gro_complete(skb);
|
||||
}
|
||||
|
||||
napi->gro_count = 0;
|
||||
napi->gro_list = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(napi_gro_flush);
|
||||
|
||||
void *skb_gro_header(struct sk_buff *skb, unsigned int hlen)
|
||||
{
|
||||
unsigned int offset = skb_gro_offset(skb);
|
||||
|
||||
hlen += offset;
|
||||
if (hlen <= skb_headlen(skb))
|
||||
return skb->data + offset;
|
||||
|
||||
if (unlikely(!skb_shinfo(skb)->nr_frags ||
|
||||
skb_shinfo(skb)->frags[0].size <=
|
||||
hlen - skb_headlen(skb) ||
|
||||
PageHighMem(skb_shinfo(skb)->frags[0].page)))
|
||||
return pskb_may_pull(skb, hlen) ? skb->data + offset : NULL;
|
||||
|
||||
return page_address(skb_shinfo(skb)->frags[0].page) +
|
||||
skb_shinfo(skb)->frags[0].page_offset +
|
||||
offset - skb_headlen(skb);
|
||||
}
|
||||
EXPORT_SYMBOL(skb_gro_header);
|
||||
|
||||
int dev_gro_receive(struct napi_struct *napi, struct sk_buff *skb)
|
||||
{
|
||||
struct sk_buff **pp = NULL;
|
||||
struct packet_type *ptype;
|
||||
__be16 type = skb->protocol;
|
||||
struct list_head *head = &ptype_base[ntohs(type) & PTYPE_HASH_MASK];
|
||||
int count = 0;
|
||||
int same_flow;
|
||||
int mac_len;
|
||||
int free;
|
||||
int ret;
|
||||
|
||||
if (!(skb->dev->features & NETIF_F_GRO))
|
||||
goto normal;
|
||||
@@ -2403,30 +2405,16 @@ int dev_gro_receive(struct napi_struct *napi, struct sk_buff *skb)
|
||||
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(ptype, head, list) {
|
||||
struct sk_buff *p;
|
||||
|
||||
if (ptype->type != type || ptype->dev || !ptype->gro_receive)
|
||||
continue;
|
||||
|
||||
skb_reset_network_header(skb);
|
||||
skb_set_network_header(skb, skb_gro_offset(skb));
|
||||
mac_len = skb->network_header - skb->mac_header;
|
||||
skb->mac_len = mac_len;
|
||||
NAPI_GRO_CB(skb)->same_flow = 0;
|
||||
NAPI_GRO_CB(skb)->flush = 0;
|
||||
NAPI_GRO_CB(skb)->free = 0;
|
||||
|
||||
for (p = napi->gro_list; p; p = p->next) {
|
||||
count++;
|
||||
|
||||
if (!NAPI_GRO_CB(p)->same_flow)
|
||||
continue;
|
||||
|
||||
if (p->mac_len != mac_len ||
|
||||
memcmp(skb_mac_header(p), skb_mac_header(skb),
|
||||
mac_len))
|
||||
NAPI_GRO_CB(p)->same_flow = 0;
|
||||
}
|
||||
|
||||
pp = ptype->gro_receive(&napi->gro_list, skb);
|
||||
break;
|
||||
}
|
||||
@@ -2436,7 +2424,7 @@ int dev_gro_receive(struct napi_struct *napi, struct sk_buff *skb)
|
||||
goto normal;
|
||||
|
||||
same_flow = NAPI_GRO_CB(skb)->same_flow;
|
||||
free = NAPI_GRO_CB(skb)->free;
|
||||
ret = NAPI_GRO_CB(skb)->free ? GRO_MERGED_FREE : GRO_MERGED;
|
||||
|
||||
if (pp) {
|
||||
struct sk_buff *nskb = *pp;
|
||||
@@ -2444,27 +2432,35 @@ int dev_gro_receive(struct napi_struct *napi, struct sk_buff *skb)
|
||||
*pp = nskb->next;
|
||||
nskb->next = NULL;
|
||||
napi_gro_complete(nskb);
|
||||
count--;
|
||||
napi->gro_count--;
|
||||
}
|
||||
|
||||
if (same_flow)
|
||||
goto ok;
|
||||
|
||||
if (NAPI_GRO_CB(skb)->flush || count >= MAX_GRO_SKBS) {
|
||||
__skb_push(skb, -skb_network_offset(skb));
|
||||
if (NAPI_GRO_CB(skb)->flush || napi->gro_count >= MAX_GRO_SKBS)
|
||||
goto normal;
|
||||
}
|
||||
|
||||
napi->gro_count++;
|
||||
NAPI_GRO_CB(skb)->count = 1;
|
||||
skb_shinfo(skb)->gso_size = skb->len;
|
||||
skb_shinfo(skb)->gso_size = skb_gro_len(skb);
|
||||
skb->next = napi->gro_list;
|
||||
napi->gro_list = skb;
|
||||
ret = GRO_HELD;
|
||||
|
||||
pull:
|
||||
if (unlikely(!pskb_may_pull(skb, skb_gro_offset(skb)))) {
|
||||
if (napi->gro_list == skb)
|
||||
napi->gro_list = skb->next;
|
||||
ret = GRO_DROP;
|
||||
}
|
||||
|
||||
ok:
|
||||
return free;
|
||||
return ret;
|
||||
|
||||
normal:
|
||||
return -1;
|
||||
ret = GRO_NORMAL;
|
||||
goto pull;
|
||||
}
|
||||
EXPORT_SYMBOL(dev_gro_receive);
|
||||
|
||||
@@ -2472,29 +2468,44 @@ static int __napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb)
|
||||
{
|
||||
struct sk_buff *p;
|
||||
|
||||
if (netpoll_rx_on(skb))
|
||||
return GRO_NORMAL;
|
||||
|
||||
for (p = napi->gro_list; p; p = p->next) {
|
||||
NAPI_GRO_CB(p)->same_flow = 1;
|
||||
NAPI_GRO_CB(p)->same_flow = !compare_ether_header(
|
||||
skb_mac_header(p), skb_gro_mac_header(skb));
|
||||
NAPI_GRO_CB(p)->flush = 0;
|
||||
}
|
||||
|
||||
return dev_gro_receive(napi, skb);
|
||||
}
|
||||
|
||||
int napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb)
|
||||
int napi_skb_finish(int ret, struct sk_buff *skb)
|
||||
{
|
||||
if (netpoll_receive_skb(skb))
|
||||
return NET_RX_DROP;
|
||||
int err = NET_RX_SUCCESS;
|
||||
|
||||
switch (__napi_gro_receive(napi, skb)) {
|
||||
case -1:
|
||||
switch (ret) {
|
||||
case GRO_NORMAL:
|
||||
return netif_receive_skb(skb);
|
||||
|
||||
case 1:
|
||||
case GRO_DROP:
|
||||
err = NET_RX_DROP;
|
||||
/* fall through */
|
||||
|
||||
case GRO_MERGED_FREE:
|
||||
kfree_skb(skb);
|
||||
break;
|
||||
}
|
||||
|
||||
return NET_RX_SUCCESS;
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(napi_skb_finish);
|
||||
|
||||
int napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb)
|
||||
{
|
||||
skb_gro_reset_offset(skb);
|
||||
|
||||
return napi_skb_finish(__napi_gro_receive(napi, skb), skb);
|
||||
}
|
||||
EXPORT_SYMBOL(napi_gro_receive);
|
||||
|
||||
@@ -2512,6 +2523,9 @@ struct sk_buff *napi_fraginfo_skb(struct napi_struct *napi,
|
||||
{
|
||||
struct net_device *dev = napi->dev;
|
||||
struct sk_buff *skb = napi->skb;
|
||||
struct ethhdr *eth;
|
||||
skb_frag_t *frag;
|
||||
int i;
|
||||
|
||||
napi->skb = NULL;
|
||||
|
||||
@@ -2524,20 +2538,36 @@ struct sk_buff *napi_fraginfo_skb(struct napi_struct *napi,
|
||||
}
|
||||
|
||||
BUG_ON(info->nr_frags > MAX_SKB_FRAGS);
|
||||
frag = &info->frags[info->nr_frags - 1];
|
||||
|
||||
for (i = skb_shinfo(skb)->nr_frags; i < info->nr_frags; i++) {
|
||||
skb_fill_page_desc(skb, i, frag->page, frag->page_offset,
|
||||
frag->size);
|
||||
frag++;
|
||||
}
|
||||
skb_shinfo(skb)->nr_frags = info->nr_frags;
|
||||
memcpy(skb_shinfo(skb)->frags, info->frags, sizeof(info->frags));
|
||||
|
||||
skb->data_len = info->len;
|
||||
skb->len += info->len;
|
||||
skb->truesize += info->len;
|
||||
|
||||
if (!pskb_may_pull(skb, ETH_HLEN)) {
|
||||
skb_reset_mac_header(skb);
|
||||
skb_gro_reset_offset(skb);
|
||||
|
||||
eth = skb_gro_header(skb, sizeof(*eth));
|
||||
if (!eth) {
|
||||
napi_reuse_skb(napi, skb);
|
||||
skb = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
skb->protocol = eth_type_trans(skb, dev);
|
||||
skb_gro_pull(skb, sizeof(*eth));
|
||||
|
||||
/*
|
||||
* This works because the only protocols we care about don't require
|
||||
* special handling. We'll fix it up properly at the end.
|
||||
*/
|
||||
skb->protocol = eth->h_proto;
|
||||
|
||||
skb->ip_summed = info->ip_summed;
|
||||
skb->csum = info->csum;
|
||||
@@ -2547,31 +2577,42 @@ out:
|
||||
}
|
||||
EXPORT_SYMBOL(napi_fraginfo_skb);
|
||||
|
||||
int napi_frags_finish(struct napi_struct *napi, struct sk_buff *skb, int ret)
|
||||
{
|
||||
int err = NET_RX_SUCCESS;
|
||||
|
||||
switch (ret) {
|
||||
case GRO_NORMAL:
|
||||
case GRO_HELD:
|
||||
skb->protocol = eth_type_trans(skb, napi->dev);
|
||||
|
||||
if (ret == GRO_NORMAL)
|
||||
return netif_receive_skb(skb);
|
||||
|
||||
skb_gro_pull(skb, -ETH_HLEN);
|
||||
break;
|
||||
|
||||
case GRO_DROP:
|
||||
err = NET_RX_DROP;
|
||||
/* fall through */
|
||||
|
||||
case GRO_MERGED_FREE:
|
||||
napi_reuse_skb(napi, skb);
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(napi_frags_finish);
|
||||
|
||||
int napi_gro_frags(struct napi_struct *napi, struct napi_gro_fraginfo *info)
|
||||
{
|
||||
struct sk_buff *skb = napi_fraginfo_skb(napi, info);
|
||||
int err = NET_RX_DROP;
|
||||
|
||||
if (!skb)
|
||||
goto out;
|
||||
return NET_RX_DROP;
|
||||
|
||||
if (netpoll_receive_skb(skb))
|
||||
goto out;
|
||||
|
||||
err = NET_RX_SUCCESS;
|
||||
|
||||
switch (__napi_gro_receive(napi, skb)) {
|
||||
case -1:
|
||||
return netif_receive_skb(skb);
|
||||
|
||||
case 0:
|
||||
goto out;
|
||||
}
|
||||
|
||||
napi_reuse_skb(napi, skb);
|
||||
|
||||
out:
|
||||
return err;
|
||||
return napi_frags_finish(napi, skb, __napi_gro_receive(napi, skb));
|
||||
}
|
||||
EXPORT_SYMBOL(napi_gro_frags);
|
||||
|
||||
@@ -2594,11 +2635,9 @@ static int process_backlog(struct napi_struct *napi, int quota)
|
||||
}
|
||||
local_irq_enable();
|
||||
|
||||
napi_gro_receive(napi, skb);
|
||||
netif_receive_skb(skb);
|
||||
} while (++work < quota && jiffies == start_time);
|
||||
|
||||
napi_gro_flush(napi);
|
||||
|
||||
return work;
|
||||
}
|
||||
|
||||
@@ -2652,6 +2691,7 @@ void netif_napi_add(struct net_device *dev, struct napi_struct *napi,
|
||||
int (*poll)(struct napi_struct *, int), int weight)
|
||||
{
|
||||
INIT_LIST_HEAD(&napi->poll_list);
|
||||
napi->gro_count = 0;
|
||||
napi->gro_list = NULL;
|
||||
napi->skb = NULL;
|
||||
napi->poll = poll;
|
||||
@@ -2671,7 +2711,7 @@ void netif_napi_del(struct napi_struct *napi)
|
||||
struct sk_buff *skb, *next;
|
||||
|
||||
list_del_init(&napi->dev_list);
|
||||
kfree(napi->skb);
|
||||
kfree_skb(napi->skb);
|
||||
|
||||
for (skb = napi->gro_list; skb; skb = next) {
|
||||
next = skb->next;
|
||||
@@ -2680,6 +2720,7 @@ void netif_napi_del(struct napi_struct *napi)
|
||||
}
|
||||
|
||||
napi->gro_list = NULL;
|
||||
napi->gro_count = 0;
|
||||
}
|
||||
EXPORT_SYMBOL(netif_napi_del);
|
||||
|
||||
@@ -3948,6 +3989,7 @@ static int dev_ifsioc(struct net *net, struct ifreq *ifr, unsigned int cmd)
|
||||
cmd == SIOCSMIIREG ||
|
||||
cmd == SIOCBRADDIF ||
|
||||
cmd == SIOCBRDELIF ||
|
||||
cmd == SIOCSHWTSTAMP ||
|
||||
cmd == SIOCWANDEV) {
|
||||
err = -EOPNOTSUPP;
|
||||
if (ops->ndo_do_ioctl) {
|
||||
@@ -4102,6 +4144,7 @@ int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg)
|
||||
case SIOCBONDCHANGEACTIVE:
|
||||
case SIOCBRADDIF:
|
||||
case SIOCBRDELIF:
|
||||
case SIOCSHWTSTAMP:
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
/* fall through */
|
||||
@@ -5198,6 +5241,7 @@ static int __init net_dev_init(void)
|
||||
queue->backlog.poll = process_backlog;
|
||||
queue->backlog.weight = weight_p;
|
||||
queue->backlog.gro_list = NULL;
|
||||
queue->backlog.gro_count = 0;
|
||||
}
|
||||
|
||||
dev_boot_phase = 0;
|
||||
@@ -5230,6 +5274,14 @@ out:
|
||||
|
||||
subsys_initcall(net_dev_init);
|
||||
|
||||
static int __init initialize_hashrnd(void)
|
||||
{
|
||||
get_random_bytes(&skb_tx_hashrnd, sizeof(skb_tx_hashrnd));
|
||||
return 0;
|
||||
}
|
||||
|
||||
late_initcall_sync(initialize_hashrnd);
|
||||
|
||||
EXPORT_SYMBOL(__dev_get_by_index);
|
||||
EXPORT_SYMBOL(__dev_get_by_name);
|
||||
EXPORT_SYMBOL(__dev_remove_pack);
|
||||
|
||||
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
* Monitoring code for network dropped packet alerts
|
||||
*
|
||||
* Copyright (C) 2009 Neil Horman <nhorman@tuxdriver.com>
|
||||
*/
|
||||
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/inetdevice.h>
|
||||
#include <linux/inet.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/netpoll.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/net_dropmon.h>
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <net/genetlink.h>
|
||||
|
||||
#include <trace/skb.h>
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#define TRACE_ON 1
|
||||
#define TRACE_OFF 0
|
||||
|
||||
static void send_dm_alert(struct work_struct *unused);
|
||||
|
||||
|
||||
/*
|
||||
* Globals, our netlink socket pointer
|
||||
* and the work handle that will send up
|
||||
* netlink alerts
|
||||
*/
|
||||
struct sock *dm_sock;
|
||||
|
||||
struct per_cpu_dm_data {
|
||||
struct work_struct dm_alert_work;
|
||||
struct sk_buff *skb;
|
||||
atomic_t dm_hit_count;
|
||||
struct timer_list send_timer;
|
||||
};
|
||||
|
||||
static struct genl_family net_drop_monitor_family = {
|
||||
.id = GENL_ID_GENERATE,
|
||||
.hdrsize = 0,
|
||||
.name = "NET_DM",
|
||||
.version = 1,
|
||||
.maxattr = NET_DM_CMD_MAX,
|
||||
};
|
||||
|
||||
static DEFINE_PER_CPU(struct per_cpu_dm_data, dm_cpu_data);
|
||||
|
||||
static int dm_hit_limit = 64;
|
||||
static int dm_delay = 1;
|
||||
|
||||
|
||||
static void reset_per_cpu_data(struct per_cpu_dm_data *data)
|
||||
{
|
||||
size_t al;
|
||||
struct net_dm_alert_msg *msg;
|
||||
|
||||
al = sizeof(struct net_dm_alert_msg);
|
||||
al += dm_hit_limit * sizeof(struct net_dm_drop_point);
|
||||
data->skb = genlmsg_new(al, GFP_KERNEL);
|
||||
genlmsg_put(data->skb, 0, 0, &net_drop_monitor_family,
|
||||
0, NET_DM_CMD_ALERT);
|
||||
msg = __nla_reserve_nohdr(data->skb, sizeof(struct net_dm_alert_msg));
|
||||
memset(msg, 0, al);
|
||||
atomic_set(&data->dm_hit_count, dm_hit_limit);
|
||||
}
|
||||
|
||||
static void send_dm_alert(struct work_struct *unused)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct per_cpu_dm_data *data = &__get_cpu_var(dm_cpu_data);
|
||||
|
||||
/*
|
||||
* Grab the skb we're about to send
|
||||
*/
|
||||
skb = data->skb;
|
||||
|
||||
/*
|
||||
* Replace it with a new one
|
||||
*/
|
||||
reset_per_cpu_data(data);
|
||||
|
||||
/*
|
||||
* Ship it!
|
||||
*/
|
||||
genlmsg_multicast(skb, 0, NET_DM_GRP_ALERT, GFP_KERNEL);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the timer function to delay the sending of an alert
|
||||
* in the event that more drops will arrive during the
|
||||
* hysteresis period. Note that it operates under the timer interrupt
|
||||
* so we don't need to disable preemption here
|
||||
*/
|
||||
static void sched_send_work(unsigned long unused)
|
||||
{
|
||||
struct per_cpu_dm_data *data = &__get_cpu_var(dm_cpu_data);
|
||||
|
||||
schedule_work(&data->dm_alert_work);
|
||||
}
|
||||
|
||||
static void trace_kfree_skb_hit(struct sk_buff *skb, void *location)
|
||||
{
|
||||
struct net_dm_alert_msg *msg;
|
||||
struct nlmsghdr *nlh;
|
||||
int i;
|
||||
struct per_cpu_dm_data *data = &__get_cpu_var(dm_cpu_data);
|
||||
|
||||
|
||||
if (!atomic_add_unless(&data->dm_hit_count, -1, 0)) {
|
||||
/*
|
||||
* we're already at zero, discard this hit
|
||||
*/
|
||||
goto out;
|
||||
}
|
||||
|
||||
nlh = (struct nlmsghdr *)data->skb->data;
|
||||
msg = genlmsg_data(nlmsg_data(nlh));
|
||||
for (i = 0; i < msg->entries; i++) {
|
||||
if (!memcmp(&location, msg->points[i].pc, sizeof(void *))) {
|
||||
msg->points[i].count++;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We need to create a new entry
|
||||
*/
|
||||
__nla_reserve_nohdr(data->skb, sizeof(struct net_dm_drop_point));
|
||||
memcpy(msg->points[msg->entries].pc, &location, sizeof(void *));
|
||||
msg->points[msg->entries].count = 1;
|
||||
msg->entries++;
|
||||
|
||||
if (!timer_pending(&data->send_timer)) {
|
||||
data->send_timer.expires = jiffies + dm_delay * HZ;
|
||||
add_timer_on(&data->send_timer, smp_processor_id());
|
||||
}
|
||||
|
||||
out:
|
||||
return;
|
||||
}
|
||||
|
||||
static int set_all_monitor_traces(int state)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
switch (state) {
|
||||
case TRACE_ON:
|
||||
rc |= register_trace_kfree_skb(trace_kfree_skb_hit);
|
||||
break;
|
||||
case TRACE_OFF:
|
||||
rc |= unregister_trace_kfree_skb(trace_kfree_skb_hit);
|
||||
|
||||
tracepoint_synchronize_unregister();
|
||||
break;
|
||||
default:
|
||||
rc = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (rc)
|
||||
return -EINPROGRESS;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static int net_dm_cmd_config(struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
static int net_dm_cmd_trace(struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
switch (info->genlhdr->cmd) {
|
||||
case NET_DM_CMD_START:
|
||||
return set_all_monitor_traces(TRACE_ON);
|
||||
break;
|
||||
case NET_DM_CMD_STOP:
|
||||
return set_all_monitor_traces(TRACE_OFF);
|
||||
break;
|
||||
}
|
||||
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
|
||||
static struct genl_ops dropmon_ops[] = {
|
||||
{
|
||||
.cmd = NET_DM_CMD_CONFIG,
|
||||
.doit = net_dm_cmd_config,
|
||||
},
|
||||
{
|
||||
.cmd = NET_DM_CMD_START,
|
||||
.doit = net_dm_cmd_trace,
|
||||
},
|
||||
{
|
||||
.cmd = NET_DM_CMD_STOP,
|
||||
.doit = net_dm_cmd_trace,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init init_net_drop_monitor(void)
|
||||
{
|
||||
int cpu;
|
||||
int rc, i, ret;
|
||||
struct per_cpu_dm_data *data;
|
||||
printk(KERN_INFO "Initalizing network drop monitor service\n");
|
||||
|
||||
if (sizeof(void *) > 8) {
|
||||
printk(KERN_ERR "Unable to store program counters on this arch, Drop monitor failed\n");
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
if (genl_register_family(&net_drop_monitor_family) < 0) {
|
||||
printk(KERN_ERR "Could not create drop monitor netlink family\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
rc = -EFAULT;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(dropmon_ops); i++) {
|
||||
ret = genl_register_ops(&net_drop_monitor_family,
|
||||
&dropmon_ops[i]);
|
||||
if (ret) {
|
||||
printk(KERN_CRIT "failed to register operation %d\n",
|
||||
dropmon_ops[i].cmd);
|
||||
goto out_unreg;
|
||||
}
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
|
||||
for_each_present_cpu(cpu) {
|
||||
data = &per_cpu(dm_cpu_data, cpu);
|
||||
reset_per_cpu_data(data);
|
||||
INIT_WORK(&data->dm_alert_work, send_dm_alert);
|
||||
init_timer(&data->send_timer);
|
||||
data->send_timer.data = cpu;
|
||||
data->send_timer.function = sched_send_work;
|
||||
}
|
||||
goto out;
|
||||
|
||||
out_unreg:
|
||||
genl_unregister_family(&net_drop_monitor_family);
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
late_initcall(init_net_drop_monitor);
|
||||
+48
-10
@@ -209,34 +209,62 @@ static int ethtool_get_drvinfo(struct net_device *dev, void __user *useraddr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ethtool_set_rxhash(struct net_device *dev, void __user *useraddr)
|
||||
static int ethtool_set_rxnfc(struct net_device *dev, void __user *useraddr)
|
||||
{
|
||||
struct ethtool_rxnfc cmd;
|
||||
|
||||
if (!dev->ethtool_ops->set_rxhash)
|
||||
if (!dev->ethtool_ops->set_rxnfc)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (copy_from_user(&cmd, useraddr, sizeof(cmd)))
|
||||
return -EFAULT;
|
||||
|
||||
return dev->ethtool_ops->set_rxhash(dev, &cmd);
|
||||
return dev->ethtool_ops->set_rxnfc(dev, &cmd);
|
||||
}
|
||||
|
||||
static int ethtool_get_rxhash(struct net_device *dev, void __user *useraddr)
|
||||
static int ethtool_get_rxnfc(struct net_device *dev, void __user *useraddr)
|
||||
{
|
||||
struct ethtool_rxnfc info;
|
||||
const struct ethtool_ops *ops = dev->ethtool_ops;
|
||||
int ret;
|
||||
void *rule_buf = NULL;
|
||||
|
||||
if (!dev->ethtool_ops->get_rxhash)
|
||||
if (!ops->get_rxnfc)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (copy_from_user(&info, useraddr, sizeof(info)))
|
||||
return -EFAULT;
|
||||
|
||||
dev->ethtool_ops->get_rxhash(dev, &info);
|
||||
if (info.cmd == ETHTOOL_GRXCLSRLALL) {
|
||||
if (info.rule_cnt > 0) {
|
||||
rule_buf = kmalloc(info.rule_cnt * sizeof(u32),
|
||||
GFP_USER);
|
||||
if (!rule_buf)
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
ret = ops->get_rxnfc(dev, &info, rule_buf);
|
||||
if (ret < 0)
|
||||
goto err_out;
|
||||
|
||||
ret = -EFAULT;
|
||||
if (copy_to_user(useraddr, &info, sizeof(info)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
goto err_out;
|
||||
|
||||
if (rule_buf) {
|
||||
useraddr += offsetof(struct ethtool_rxnfc, rule_locs);
|
||||
if (copy_to_user(useraddr, rule_buf,
|
||||
info.rule_cnt * sizeof(u32)))
|
||||
goto err_out;
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
err_out:
|
||||
if (rule_buf)
|
||||
kfree(rule_buf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ethtool_get_regs(struct net_device *dev, char __user *useraddr)
|
||||
@@ -901,6 +929,10 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
|
||||
case ETHTOOL_GFLAGS:
|
||||
case ETHTOOL_GPFLAGS:
|
||||
case ETHTOOL_GRXFH:
|
||||
case ETHTOOL_GRXRINGS:
|
||||
case ETHTOOL_GRXCLSRLCNT:
|
||||
case ETHTOOL_GRXCLSRULE:
|
||||
case ETHTOOL_GRXCLSRLALL:
|
||||
break;
|
||||
default:
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
@@ -1052,10 +1084,16 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
|
||||
dev->ethtool_ops->set_priv_flags);
|
||||
break;
|
||||
case ETHTOOL_GRXFH:
|
||||
rc = ethtool_get_rxhash(dev, useraddr);
|
||||
case ETHTOOL_GRXRINGS:
|
||||
case ETHTOOL_GRXCLSRLCNT:
|
||||
case ETHTOOL_GRXCLSRULE:
|
||||
case ETHTOOL_GRXCLSRLALL:
|
||||
rc = ethtool_get_rxnfc(dev, useraddr);
|
||||
break;
|
||||
case ETHTOOL_SRXFH:
|
||||
rc = ethtool_set_rxhash(dev, useraddr);
|
||||
case ETHTOOL_SRXCLSRLDEL:
|
||||
case ETHTOOL_SRXCLSRLINS:
|
||||
rc = ethtool_set_rxnfc(dev, useraddr);
|
||||
break;
|
||||
case ETHTOOL_GGRO:
|
||||
rc = ethtool_get_gro(dev, useraddr);
|
||||
|
||||
@@ -588,7 +588,8 @@ static void notify_rule_change(int event, struct fib_rule *rule,
|
||||
goto errout;
|
||||
}
|
||||
|
||||
err = rtnl_notify(skb, net, pid, ops->nlgroup, nlh, GFP_KERNEL);
|
||||
rtnl_notify(skb, net, pid, ops->nlgroup, nlh, GFP_KERNEL);
|
||||
return;
|
||||
errout:
|
||||
if (err < 0)
|
||||
rtnl_set_sk_err(net, ops->nlgroup, err);
|
||||
|
||||
@@ -871,8 +871,7 @@ static void neigh_timer_handler(unsigned long arg)
|
||||
write_unlock(&neigh->lock);
|
||||
neigh->ops->solicit(neigh, skb);
|
||||
atomic_inc(&neigh->probes);
|
||||
if (skb)
|
||||
kfree_skb(skb);
|
||||
kfree_skb(skb);
|
||||
} else {
|
||||
out:
|
||||
write_unlock(&neigh->lock);
|
||||
@@ -908,8 +907,7 @@ int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb)
|
||||
neigh->updated = jiffies;
|
||||
write_unlock_bh(&neigh->lock);
|
||||
|
||||
if (skb)
|
||||
kfree_skb(skb);
|
||||
kfree_skb(skb);
|
||||
return 1;
|
||||
}
|
||||
} else if (neigh->nud_state & NUD_STALE) {
|
||||
@@ -1656,7 +1654,11 @@ static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
|
||||
flags &= ~NEIGH_UPDATE_F_OVERRIDE;
|
||||
}
|
||||
|
||||
err = neigh_update(neigh, lladdr, ndm->ndm_state, flags);
|
||||
if (ndm->ndm_flags & NTF_USE) {
|
||||
neigh_event_send(neigh, NULL);
|
||||
err = 0;
|
||||
} else
|
||||
err = neigh_update(neigh, lladdr, ndm->ndm_state, flags);
|
||||
neigh_release(neigh);
|
||||
goto out_dev_put;
|
||||
}
|
||||
@@ -2534,7 +2536,8 @@ static void __neigh_notify(struct neighbour *n, int type, int flags)
|
||||
kfree_skb(skb);
|
||||
goto errout;
|
||||
}
|
||||
err = rtnl_notify(skb, net, 0, RTNLGRP_NEIGH, NULL, GFP_ATOMIC);
|
||||
rtnl_notify(skb, net, 0, RTNLGRP_NEIGH, NULL, GFP_ATOMIC);
|
||||
return;
|
||||
errout:
|
||||
if (err < 0)
|
||||
rtnl_set_sk_err(net, RTNLGRP_NEIGH, err);
|
||||
|
||||
@@ -498,7 +498,7 @@ int netdev_register_kobject(struct net_device *net)
|
||||
dev->groups = groups;
|
||||
|
||||
BUILD_BUG_ON(BUS_ID_SIZE < IFNAMSIZ);
|
||||
dev_set_name(dev, net->name);
|
||||
dev_set_name(dev, "%s", net->name);
|
||||
|
||||
#ifdef CONFIG_SYSFS
|
||||
*groups++ = &netstat_group;
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* consolidates trace point definitions
|
||||
*
|
||||
* Copyright (C) 2009 Neil Horman <nhorman@tuxdriver.com>
|
||||
*/
|
||||
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/inetdevice.h>
|
||||
#include <linux/inet.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/netpoll.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/net_dropmon.h>
|
||||
#include <trace/skb.h>
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
#include <asm/bitops.h>
|
||||
|
||||
|
||||
DEFINE_TRACE(kfree_skb);
|
||||
EXPORT_TRACEPOINT_SYMBOL_GPL(kfree_skb);
|
||||
+6
-12
@@ -3275,8 +3275,7 @@ static void pktgen_stop(struct pktgen_thread *t)
|
||||
|
||||
list_for_each_entry(pkt_dev, &t->if_list, list) {
|
||||
pktgen_stop_device(pkt_dev);
|
||||
if (pkt_dev->skb)
|
||||
kfree_skb(pkt_dev->skb);
|
||||
kfree_skb(pkt_dev->skb);
|
||||
|
||||
pkt_dev->skb = NULL;
|
||||
}
|
||||
@@ -3303,8 +3302,7 @@ static void pktgen_rem_one_if(struct pktgen_thread *t)
|
||||
if (!cur->removal_mark)
|
||||
continue;
|
||||
|
||||
if (cur->skb)
|
||||
kfree_skb(cur->skb);
|
||||
kfree_skb(cur->skb);
|
||||
cur->skb = NULL;
|
||||
|
||||
pktgen_remove_device(t, cur);
|
||||
@@ -3328,8 +3326,7 @@ static void pktgen_rem_all_ifs(struct pktgen_thread *t)
|
||||
list_for_each_safe(q, n, &t->if_list) {
|
||||
cur = list_entry(q, struct pktgen_dev, list);
|
||||
|
||||
if (cur->skb)
|
||||
kfree_skb(cur->skb);
|
||||
kfree_skb(cur->skb);
|
||||
cur->skb = NULL;
|
||||
|
||||
pktgen_remove_device(t, cur);
|
||||
@@ -3393,8 +3390,7 @@ static __inline__ void pktgen_xmit(struct pktgen_dev *pkt_dev)
|
||||
|
||||
if (!netif_running(odev)) {
|
||||
pktgen_stop_device(pkt_dev);
|
||||
if (pkt_dev->skb)
|
||||
kfree_skb(pkt_dev->skb);
|
||||
kfree_skb(pkt_dev->skb);
|
||||
pkt_dev->skb = NULL;
|
||||
goto out;
|
||||
}
|
||||
@@ -3415,8 +3411,7 @@ static __inline__ void pktgen_xmit(struct pktgen_dev *pkt_dev)
|
||||
if ((++pkt_dev->clone_count >= pkt_dev->clone_skb)
|
||||
|| (!pkt_dev->skb)) {
|
||||
/* build a new pkt */
|
||||
if (pkt_dev->skb)
|
||||
kfree_skb(pkt_dev->skb);
|
||||
kfree_skb(pkt_dev->skb);
|
||||
|
||||
pkt_dev->skb = fill_packet(odev, pkt_dev);
|
||||
if (pkt_dev->skb == NULL) {
|
||||
@@ -3498,8 +3493,7 @@ static __inline__ void pktgen_xmit(struct pktgen_dev *pkt_dev)
|
||||
|
||||
/* Done with this */
|
||||
pktgen_stop_device(pkt_dev);
|
||||
if (pkt_dev->skb)
|
||||
kfree_skb(pkt_dev->skb);
|
||||
kfree_skb(pkt_dev->skb);
|
||||
pkt_dev->skb = NULL;
|
||||
}
|
||||
out:;
|
||||
|
||||
@@ -455,8 +455,8 @@ int rtnl_unicast(struct sk_buff *skb, struct net *net, u32 pid)
|
||||
return nlmsg_unicast(rtnl, skb, pid);
|
||||
}
|
||||
|
||||
int rtnl_notify(struct sk_buff *skb, struct net *net, u32 pid, u32 group,
|
||||
struct nlmsghdr *nlh, gfp_t flags)
|
||||
void rtnl_notify(struct sk_buff *skb, struct net *net, u32 pid, u32 group,
|
||||
struct nlmsghdr *nlh, gfp_t flags)
|
||||
{
|
||||
struct sock *rtnl = net->rtnl;
|
||||
int report = 0;
|
||||
@@ -464,7 +464,7 @@ int rtnl_notify(struct sk_buff *skb, struct net *net, u32 pid, u32 group,
|
||||
if (nlh)
|
||||
report = nlmsg_report(nlh);
|
||||
|
||||
return nlmsg_notify(rtnl, skb, pid, group, report, flags);
|
||||
nlmsg_notify(rtnl, skb, pid, group, report, flags);
|
||||
}
|
||||
|
||||
void rtnl_set_sk_err(struct net *net, u32 group, int error)
|
||||
@@ -1246,7 +1246,8 @@ void rtmsg_ifinfo(int type, struct net_device *dev, unsigned change)
|
||||
kfree_skb(skb);
|
||||
goto errout;
|
||||
}
|
||||
err = rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, GFP_KERNEL);
|
||||
rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, GFP_KERNEL);
|
||||
return;
|
||||
errout:
|
||||
if (err < 0)
|
||||
rtnl_set_sk_err(net, RTNLGRP_LINK, err);
|
||||
|
||||
+170
-65
@@ -55,6 +55,7 @@
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/errqueue.h>
|
||||
|
||||
#include <net/protocol.h>
|
||||
#include <net/dst.h>
|
||||
@@ -64,6 +65,7 @@
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/system.h>
|
||||
#include <trace/skb.h>
|
||||
|
||||
#include "kmap_skb.h"
|
||||
|
||||
@@ -123,6 +125,7 @@ void skb_over_panic(struct sk_buff *skb, int sz, void *here)
|
||||
skb->dev ? skb->dev->name : "<NULL>");
|
||||
BUG();
|
||||
}
|
||||
EXPORT_SYMBOL(skb_over_panic);
|
||||
|
||||
/**
|
||||
* skb_under_panic - private function
|
||||
@@ -142,6 +145,7 @@ void skb_under_panic(struct sk_buff *skb, int sz, void *here)
|
||||
skb->dev ? skb->dev->name : "<NULL>");
|
||||
BUG();
|
||||
}
|
||||
EXPORT_SYMBOL(skb_under_panic);
|
||||
|
||||
/* Allocate a new skbuff. We do this ourselves so we can fill in a few
|
||||
* 'private' fields and also do memory statistics to find all the
|
||||
@@ -205,7 +209,9 @@ struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask,
|
||||
shinfo->gso_segs = 0;
|
||||
shinfo->gso_type = 0;
|
||||
shinfo->ip6_frag_id = 0;
|
||||
shinfo->tx_flags.flags = 0;
|
||||
shinfo->frag_list = NULL;
|
||||
memset(&shinfo->hwtstamps, 0, sizeof(shinfo->hwtstamps));
|
||||
|
||||
if (fclone) {
|
||||
struct sk_buff *child = skb + 1;
|
||||
@@ -223,6 +229,7 @@ nodata:
|
||||
skb = NULL;
|
||||
goto out;
|
||||
}
|
||||
EXPORT_SYMBOL(__alloc_skb);
|
||||
|
||||
/**
|
||||
* __netdev_alloc_skb - allocate an skbuff for rx on a specific device
|
||||
@@ -250,6 +257,7 @@ struct sk_buff *__netdev_alloc_skb(struct net_device *dev,
|
||||
}
|
||||
return skb;
|
||||
}
|
||||
EXPORT_SYMBOL(__netdev_alloc_skb);
|
||||
|
||||
struct page *__netdev_alloc_page(struct net_device *dev, gfp_t gfp_mask)
|
||||
{
|
||||
@@ -418,6 +426,7 @@ void __kfree_skb(struct sk_buff *skb)
|
||||
skb_release_all(skb);
|
||||
kfree_skbmem(skb);
|
||||
}
|
||||
EXPORT_SYMBOL(__kfree_skb);
|
||||
|
||||
/**
|
||||
* kfree_skb - free an sk_buff
|
||||
@@ -434,8 +443,30 @@ void kfree_skb(struct sk_buff *skb)
|
||||
smp_rmb();
|
||||
else if (likely(!atomic_dec_and_test(&skb->users)))
|
||||
return;
|
||||
trace_kfree_skb(skb, __builtin_return_address(0));
|
||||
__kfree_skb(skb);
|
||||
}
|
||||
EXPORT_SYMBOL(kfree_skb);
|
||||
|
||||
/**
|
||||
* consume_skb - free an skbuff
|
||||
* @skb: buffer to free
|
||||
*
|
||||
* Drop a ref to the buffer and free it if the usage count has hit zero
|
||||
* Functions identically to kfree_skb, but kfree_skb assumes that the frame
|
||||
* is being dropped after a failure and notes that
|
||||
*/
|
||||
void consume_skb(struct sk_buff *skb)
|
||||
{
|
||||
if (unlikely(!skb))
|
||||
return;
|
||||
if (likely(atomic_read(&skb->users) == 1))
|
||||
smp_rmb();
|
||||
else if (likely(!atomic_dec_and_test(&skb->users)))
|
||||
return;
|
||||
__kfree_skb(skb);
|
||||
}
|
||||
EXPORT_SYMBOL(consume_skb);
|
||||
|
||||
/**
|
||||
* skb_recycle_check - check if skb can be reused for receive
|
||||
@@ -605,6 +636,7 @@ struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t gfp_mask)
|
||||
|
||||
return __skb_clone(n, skb);
|
||||
}
|
||||
EXPORT_SYMBOL(skb_clone);
|
||||
|
||||
static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
|
||||
{
|
||||
@@ -671,7 +703,7 @@ struct sk_buff *skb_copy(const struct sk_buff *skb, gfp_t gfp_mask)
|
||||
copy_skb_header(n, skb);
|
||||
return n;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(skb_copy);
|
||||
|
||||
/**
|
||||
* pskb_copy - create copy of an sk_buff with private head.
|
||||
@@ -730,6 +762,7 @@ struct sk_buff *pskb_copy(struct sk_buff *skb, gfp_t gfp_mask)
|
||||
out:
|
||||
return n;
|
||||
}
|
||||
EXPORT_SYMBOL(pskb_copy);
|
||||
|
||||
/**
|
||||
* pskb_expand_head - reallocate header of &sk_buff
|
||||
@@ -813,6 +846,7 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail,
|
||||
nodata:
|
||||
return -ENOMEM;
|
||||
}
|
||||
EXPORT_SYMBOL(pskb_expand_head);
|
||||
|
||||
/* Make private copy of skb with writable head and some headroom */
|
||||
|
||||
@@ -833,7 +867,7 @@ struct sk_buff *skb_realloc_headroom(struct sk_buff *skb, unsigned int headroom)
|
||||
}
|
||||
return skb2;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(skb_realloc_headroom);
|
||||
|
||||
/**
|
||||
* skb_copy_expand - copy and expand sk_buff
|
||||
@@ -898,6 +932,7 @@ struct sk_buff *skb_copy_expand(const struct sk_buff *skb,
|
||||
|
||||
return n;
|
||||
}
|
||||
EXPORT_SYMBOL(skb_copy_expand);
|
||||
|
||||
/**
|
||||
* skb_pad - zero pad the tail of an skb
|
||||
@@ -943,6 +978,7 @@ free_skb:
|
||||
kfree_skb(skb);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(skb_pad);
|
||||
|
||||
/**
|
||||
* skb_put - add data to a buffer
|
||||
@@ -1100,6 +1136,7 @@ done:
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(___pskb_trim);
|
||||
|
||||
/**
|
||||
* __pskb_pull_tail - advance tail of skb header
|
||||
@@ -1193,8 +1230,7 @@ unsigned char *__pskb_pull_tail(struct sk_buff *skb, int delta)
|
||||
insp = list;
|
||||
}
|
||||
if (!pskb_pull(list, eat)) {
|
||||
if (clone)
|
||||
kfree_skb(clone);
|
||||
kfree_skb(clone);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
@@ -1238,6 +1274,7 @@ pull_pages:
|
||||
|
||||
return skb_tail_pointer(skb);
|
||||
}
|
||||
EXPORT_SYMBOL(__pskb_pull_tail);
|
||||
|
||||
/* Copy some data bits from skb to kernel buffer. */
|
||||
|
||||
@@ -1315,6 +1352,7 @@ int skb_copy_bits(const struct sk_buff *skb, int offset, void *to, int len)
|
||||
fault:
|
||||
return -EFAULT;
|
||||
}
|
||||
EXPORT_SYMBOL(skb_copy_bits);
|
||||
|
||||
/*
|
||||
* Callback from splice_to_pipe(), if we need to release some pages
|
||||
@@ -1325,14 +1363,39 @@ static void sock_spd_release(struct splice_pipe_desc *spd, unsigned int i)
|
||||
put_page(spd->pages[i]);
|
||||
}
|
||||
|
||||
static inline struct page *linear_to_page(struct page *page, unsigned int len,
|
||||
unsigned int offset)
|
||||
static inline struct page *linear_to_page(struct page *page, unsigned int *len,
|
||||
unsigned int *offset,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct page *p = alloc_pages(GFP_KERNEL, 0);
|
||||
struct sock *sk = skb->sk;
|
||||
struct page *p = sk->sk_sndmsg_page;
|
||||
unsigned int off;
|
||||
|
||||
if (!p)
|
||||
return NULL;
|
||||
memcpy(page_address(p) + offset, page_address(page) + offset, len);
|
||||
if (!p) {
|
||||
new_page:
|
||||
p = sk->sk_sndmsg_page = alloc_pages(sk->sk_allocation, 0);
|
||||
if (!p)
|
||||
return NULL;
|
||||
|
||||
off = sk->sk_sndmsg_off = 0;
|
||||
/* hold one ref to this page until it's full */
|
||||
} else {
|
||||
unsigned int mlen;
|
||||
|
||||
off = sk->sk_sndmsg_off;
|
||||
mlen = PAGE_SIZE - off;
|
||||
if (mlen < 64 && mlen < *len) {
|
||||
put_page(p);
|
||||
goto new_page;
|
||||
}
|
||||
|
||||
*len = min_t(unsigned int, *len, mlen);
|
||||
}
|
||||
|
||||
memcpy(page_address(p) + off, page_address(page) + *offset, *len);
|
||||
sk->sk_sndmsg_off += *len;
|
||||
*offset = off;
|
||||
get_page(p);
|
||||
|
||||
return p;
|
||||
}
|
||||
@@ -1341,21 +1404,21 @@ static inline struct page *linear_to_page(struct page *page, unsigned int len,
|
||||
* Fill page/offset/length into spd, if it can hold more pages.
|
||||
*/
|
||||
static inline int spd_fill_page(struct splice_pipe_desc *spd, struct page *page,
|
||||
unsigned int len, unsigned int offset,
|
||||
unsigned int *len, unsigned int offset,
|
||||
struct sk_buff *skb, int linear)
|
||||
{
|
||||
if (unlikely(spd->nr_pages == PIPE_BUFFERS))
|
||||
return 1;
|
||||
|
||||
if (linear) {
|
||||
page = linear_to_page(page, len, offset);
|
||||
page = linear_to_page(page, len, &offset, skb);
|
||||
if (!page)
|
||||
return 1;
|
||||
} else
|
||||
get_page(page);
|
||||
|
||||
spd->pages[spd->nr_pages] = page;
|
||||
spd->partial[spd->nr_pages].len = len;
|
||||
spd->partial[spd->nr_pages].len = *len;
|
||||
spd->partial[spd->nr_pages].offset = offset;
|
||||
spd->nr_pages++;
|
||||
|
||||
@@ -1365,8 +1428,13 @@ static inline int spd_fill_page(struct splice_pipe_desc *spd, struct page *page,
|
||||
static inline void __segment_seek(struct page **page, unsigned int *poff,
|
||||
unsigned int *plen, unsigned int off)
|
||||
{
|
||||
unsigned long n;
|
||||
|
||||
*poff += off;
|
||||
*page += *poff / PAGE_SIZE;
|
||||
n = *poff / PAGE_SIZE;
|
||||
if (n)
|
||||
*page = nth_page(*page, n);
|
||||
|
||||
*poff = *poff % PAGE_SIZE;
|
||||
*plen -= off;
|
||||
}
|
||||
@@ -1397,7 +1465,7 @@ static inline int __splice_segment(struct page *page, unsigned int poff,
|
||||
/* the linear region may spread across several pages */
|
||||
flen = min_t(unsigned int, flen, PAGE_SIZE - poff);
|
||||
|
||||
if (spd_fill_page(spd, page, flen, poff, skb, linear))
|
||||
if (spd_fill_page(spd, page, &flen, poff, skb, linear))
|
||||
return 1;
|
||||
|
||||
__segment_seek(&page, &poff, &plen, flen);
|
||||
@@ -1590,7 +1658,6 @@ int skb_store_bits(struct sk_buff *skb, int offset, const void *from, int len)
|
||||
fault:
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(skb_store_bits);
|
||||
|
||||
/* Checksum skb data. */
|
||||
@@ -1667,6 +1734,7 @@ __wsum skb_checksum(const struct sk_buff *skb, int offset,
|
||||
|
||||
return csum;
|
||||
}
|
||||
EXPORT_SYMBOL(skb_checksum);
|
||||
|
||||
/* Both of above in one bottle. */
|
||||
|
||||
@@ -1748,6 +1816,7 @@ __wsum skb_copy_and_csum_bits(const struct sk_buff *skb, int offset,
|
||||
BUG_ON(len);
|
||||
return csum;
|
||||
}
|
||||
EXPORT_SYMBOL(skb_copy_and_csum_bits);
|
||||
|
||||
void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to)
|
||||
{
|
||||
@@ -1774,6 +1843,7 @@ void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to)
|
||||
*((__sum16 *)(to + csstuff)) = csum_fold(csum);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(skb_copy_and_csum_dev);
|
||||
|
||||
/**
|
||||
* skb_dequeue - remove from the head of the queue
|
||||
@@ -1794,6 +1864,7 @@ struct sk_buff *skb_dequeue(struct sk_buff_head *list)
|
||||
spin_unlock_irqrestore(&list->lock, flags);
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL(skb_dequeue);
|
||||
|
||||
/**
|
||||
* skb_dequeue_tail - remove from the tail of the queue
|
||||
@@ -1813,6 +1884,7 @@ struct sk_buff *skb_dequeue_tail(struct sk_buff_head *list)
|
||||
spin_unlock_irqrestore(&list->lock, flags);
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL(skb_dequeue_tail);
|
||||
|
||||
/**
|
||||
* skb_queue_purge - empty a list
|
||||
@@ -1828,6 +1900,7 @@ void skb_queue_purge(struct sk_buff_head *list)
|
||||
while ((skb = skb_dequeue(list)) != NULL)
|
||||
kfree_skb(skb);
|
||||
}
|
||||
EXPORT_SYMBOL(skb_queue_purge);
|
||||
|
||||
/**
|
||||
* skb_queue_head - queue a buffer at the list head
|
||||
@@ -1848,6 +1921,7 @@ void skb_queue_head(struct sk_buff_head *list, struct sk_buff *newsk)
|
||||
__skb_queue_head(list, newsk);
|
||||
spin_unlock_irqrestore(&list->lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL(skb_queue_head);
|
||||
|
||||
/**
|
||||
* skb_queue_tail - queue a buffer at the list tail
|
||||
@@ -1868,6 +1942,7 @@ void skb_queue_tail(struct sk_buff_head *list, struct sk_buff *newsk)
|
||||
__skb_queue_tail(list, newsk);
|
||||
spin_unlock_irqrestore(&list->lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL(skb_queue_tail);
|
||||
|
||||
/**
|
||||
* skb_unlink - remove a buffer from a list
|
||||
@@ -1887,6 +1962,7 @@ void skb_unlink(struct sk_buff *skb, struct sk_buff_head *list)
|
||||
__skb_unlink(skb, list);
|
||||
spin_unlock_irqrestore(&list->lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL(skb_unlink);
|
||||
|
||||
/**
|
||||
* skb_append - append a buffer
|
||||
@@ -1906,7 +1982,7 @@ void skb_append(struct sk_buff *old, struct sk_buff *newsk, struct sk_buff_head
|
||||
__skb_queue_after(list, old, newsk);
|
||||
spin_unlock_irqrestore(&list->lock, flags);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(skb_append);
|
||||
|
||||
/**
|
||||
* skb_insert - insert a buffer
|
||||
@@ -1928,6 +2004,7 @@ void skb_insert(struct sk_buff *old, struct sk_buff *newsk, struct sk_buff_head
|
||||
__skb_insert(newsk, old->prev, old, list);
|
||||
spin_unlock_irqrestore(&list->lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL(skb_insert);
|
||||
|
||||
static inline void skb_split_inside_header(struct sk_buff *skb,
|
||||
struct sk_buff* skb1,
|
||||
@@ -2006,6 +2083,7 @@ void skb_split(struct sk_buff *skb, struct sk_buff *skb1, const u32 len)
|
||||
else /* Second chunk has no header, nothing to copy. */
|
||||
skb_split_no_header(skb, skb1, len, pos);
|
||||
}
|
||||
EXPORT_SYMBOL(skb_split);
|
||||
|
||||
/* Shifting from/to a cloned skb is a no-go.
|
||||
*
|
||||
@@ -2168,6 +2246,7 @@ void skb_prepare_seq_read(struct sk_buff *skb, unsigned int from,
|
||||
st->frag_idx = st->stepped_offset = 0;
|
||||
st->frag_data = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(skb_prepare_seq_read);
|
||||
|
||||
/**
|
||||
* skb_seq_read - Sequentially read skb data
|
||||
@@ -2255,6 +2334,7 @@ next_skb:
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(skb_seq_read);
|
||||
|
||||
/**
|
||||
* skb_abort_seq_read - Abort a sequential read of skb data
|
||||
@@ -2268,6 +2348,7 @@ void skb_abort_seq_read(struct skb_seq_state *st)
|
||||
if (st->frag_data)
|
||||
kunmap_skb_frag(st->frag_data);
|
||||
}
|
||||
EXPORT_SYMBOL(skb_abort_seq_read);
|
||||
|
||||
#define TS_SKB_CB(state) ((struct skb_seq_state *) &((state)->cb))
|
||||
|
||||
@@ -2310,6 +2391,7 @@ unsigned int skb_find_text(struct sk_buff *skb, unsigned int from,
|
||||
ret = textsearch_find(config, state);
|
||||
return (ret <= to - from ? ret : UINT_MAX);
|
||||
}
|
||||
EXPORT_SYMBOL(skb_find_text);
|
||||
|
||||
/**
|
||||
* skb_append_datato_frags: - append the user data to a skb
|
||||
@@ -2382,6 +2464,7 @@ int skb_append_datato_frags(struct sock *sk, struct sk_buff *skb,
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(skb_append_datato_frags);
|
||||
|
||||
/**
|
||||
* skb_pull_rcsum - pull skb and update receive checksum
|
||||
@@ -2569,7 +2652,6 @@ err:
|
||||
}
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(skb_segment);
|
||||
|
||||
int skb_gro_receive(struct sk_buff **head, struct sk_buff *skb)
|
||||
@@ -2577,17 +2659,23 @@ int skb_gro_receive(struct sk_buff **head, struct sk_buff *skb)
|
||||
struct sk_buff *p = *head;
|
||||
struct sk_buff *nskb;
|
||||
unsigned int headroom;
|
||||
unsigned int hlen = p->data - skb_mac_header(p);
|
||||
unsigned int len = skb->len;
|
||||
unsigned int len = skb_gro_len(skb);
|
||||
|
||||
if (hlen + p->len + len >= 65536)
|
||||
if (p->len + len >= 65536)
|
||||
return -E2BIG;
|
||||
|
||||
if (skb_shinfo(p)->frag_list)
|
||||
goto merge;
|
||||
else if (!skb_headlen(p) && !skb_headlen(skb) &&
|
||||
skb_shinfo(p)->nr_frags + skb_shinfo(skb)->nr_frags <
|
||||
MAX_SKB_FRAGS) {
|
||||
else if (skb_headlen(skb) <= skb_gro_offset(skb)) {
|
||||
if (skb_shinfo(p)->nr_frags + skb_shinfo(skb)->nr_frags >
|
||||
MAX_SKB_FRAGS)
|
||||
return -E2BIG;
|
||||
|
||||
skb_shinfo(skb)->frags[0].page_offset +=
|
||||
skb_gro_offset(skb) - skb_headlen(skb);
|
||||
skb_shinfo(skb)->frags[0].size -=
|
||||
skb_gro_offset(skb) - skb_headlen(skb);
|
||||
|
||||
memcpy(skb_shinfo(p)->frags + skb_shinfo(p)->nr_frags,
|
||||
skb_shinfo(skb)->frags,
|
||||
skb_shinfo(skb)->nr_frags * sizeof(skb_frag_t));
|
||||
@@ -2604,7 +2692,7 @@ int skb_gro_receive(struct sk_buff **head, struct sk_buff *skb)
|
||||
}
|
||||
|
||||
headroom = skb_headroom(p);
|
||||
nskb = netdev_alloc_skb(p->dev, headroom);
|
||||
nskb = netdev_alloc_skb(p->dev, headroom + skb_gro_offset(p));
|
||||
if (unlikely(!nskb))
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -2612,12 +2700,15 @@ int skb_gro_receive(struct sk_buff **head, struct sk_buff *skb)
|
||||
nskb->mac_len = p->mac_len;
|
||||
|
||||
skb_reserve(nskb, headroom);
|
||||
__skb_put(nskb, skb_gro_offset(p));
|
||||
|
||||
skb_set_mac_header(nskb, -hlen);
|
||||
skb_set_mac_header(nskb, skb_mac_header(p) - p->data);
|
||||
skb_set_network_header(nskb, skb_network_offset(p));
|
||||
skb_set_transport_header(nskb, skb_transport_offset(p));
|
||||
|
||||
memcpy(skb_mac_header(nskb), skb_mac_header(p), hlen);
|
||||
__skb_pull(p, skb_gro_offset(p));
|
||||
memcpy(skb_mac_header(nskb), skb_mac_header(p),
|
||||
p->data - skb_mac_header(p));
|
||||
|
||||
*NAPI_GRO_CB(nskb) = *NAPI_GRO_CB(p);
|
||||
skb_shinfo(nskb)->frag_list = p;
|
||||
@@ -2636,6 +2727,17 @@ int skb_gro_receive(struct sk_buff **head, struct sk_buff *skb)
|
||||
p = nskb;
|
||||
|
||||
merge:
|
||||
if (skb_gro_offset(skb) > skb_headlen(skb)) {
|
||||
skb_shinfo(skb)->frags[0].page_offset +=
|
||||
skb_gro_offset(skb) - skb_headlen(skb);
|
||||
skb_shinfo(skb)->frags[0].size -=
|
||||
skb_gro_offset(skb) - skb_headlen(skb);
|
||||
skb_gro_reset_offset(skb);
|
||||
skb_gro_pull(skb, skb_headlen(skb));
|
||||
}
|
||||
|
||||
__skb_pull(skb, skb_gro_offset(skb));
|
||||
|
||||
p->prev->next = skb;
|
||||
p->prev = skb;
|
||||
skb_header_release(skb);
|
||||
@@ -2747,6 +2849,7 @@ int skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int le
|
||||
|
||||
return nsg;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(skb_to_sgvec);
|
||||
|
||||
/**
|
||||
* skb_cow_data - Check that a socket buffer's data buffers are writable
|
||||
@@ -2856,6 +2959,45 @@ int skb_cow_data(struct sk_buff *skb, int tailbits, struct sk_buff **trailer)
|
||||
|
||||
return elt;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(skb_cow_data);
|
||||
|
||||
void skb_tstamp_tx(struct sk_buff *orig_skb,
|
||||
struct skb_shared_hwtstamps *hwtstamps)
|
||||
{
|
||||
struct sock *sk = orig_skb->sk;
|
||||
struct sock_exterr_skb *serr;
|
||||
struct sk_buff *skb;
|
||||
int err;
|
||||
|
||||
if (!sk)
|
||||
return;
|
||||
|
||||
skb = skb_clone(orig_skb, GFP_ATOMIC);
|
||||
if (!skb)
|
||||
return;
|
||||
|
||||
if (hwtstamps) {
|
||||
*skb_hwtstamps(skb) =
|
||||
*hwtstamps;
|
||||
} else {
|
||||
/*
|
||||
* no hardware time stamps available,
|
||||
* so keep the skb_shared_tx and only
|
||||
* store software time stamp
|
||||
*/
|
||||
skb->tstamp = ktime_get_real();
|
||||
}
|
||||
|
||||
serr = SKB_EXT_ERR(skb);
|
||||
memset(serr, 0, sizeof(*serr));
|
||||
serr->ee.ee_errno = ENOMSG;
|
||||
serr->ee.ee_origin = SO_EE_ORIGIN_TIMESTAMPING;
|
||||
err = sock_queue_err_skb(sk, skb);
|
||||
if (err)
|
||||
kfree_skb(skb);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(skb_tstamp_tx);
|
||||
|
||||
|
||||
/**
|
||||
* skb_partial_csum_set - set up and verify partial csum values for packet
|
||||
@@ -2884,6 +3026,7 @@ bool skb_partial_csum_set(struct sk_buff *skb, u16 start, u16 off)
|
||||
skb->csum_offset = off;
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(skb_partial_csum_set);
|
||||
|
||||
void __skb_warn_lro_forwarding(const struct sk_buff *skb)
|
||||
{
|
||||
@@ -2891,42 +3034,4 @@ void __skb_warn_lro_forwarding(const struct sk_buff *skb)
|
||||
pr_warning("%s: received packets cannot be forwarded"
|
||||
" while LRO is enabled\n", skb->dev->name);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(___pskb_trim);
|
||||
EXPORT_SYMBOL(__kfree_skb);
|
||||
EXPORT_SYMBOL(kfree_skb);
|
||||
EXPORT_SYMBOL(__pskb_pull_tail);
|
||||
EXPORT_SYMBOL(__alloc_skb);
|
||||
EXPORT_SYMBOL(__netdev_alloc_skb);
|
||||
EXPORT_SYMBOL(pskb_copy);
|
||||
EXPORT_SYMBOL(pskb_expand_head);
|
||||
EXPORT_SYMBOL(skb_checksum);
|
||||
EXPORT_SYMBOL(skb_clone);
|
||||
EXPORT_SYMBOL(skb_copy);
|
||||
EXPORT_SYMBOL(skb_copy_and_csum_bits);
|
||||
EXPORT_SYMBOL(skb_copy_and_csum_dev);
|
||||
EXPORT_SYMBOL(skb_copy_bits);
|
||||
EXPORT_SYMBOL(skb_copy_expand);
|
||||
EXPORT_SYMBOL(skb_over_panic);
|
||||
EXPORT_SYMBOL(skb_pad);
|
||||
EXPORT_SYMBOL(skb_realloc_headroom);
|
||||
EXPORT_SYMBOL(skb_under_panic);
|
||||
EXPORT_SYMBOL(skb_dequeue);
|
||||
EXPORT_SYMBOL(skb_dequeue_tail);
|
||||
EXPORT_SYMBOL(skb_insert);
|
||||
EXPORT_SYMBOL(skb_queue_purge);
|
||||
EXPORT_SYMBOL(skb_queue_head);
|
||||
EXPORT_SYMBOL(skb_queue_tail);
|
||||
EXPORT_SYMBOL(skb_unlink);
|
||||
EXPORT_SYMBOL(skb_append);
|
||||
EXPORT_SYMBOL(skb_split);
|
||||
EXPORT_SYMBOL(skb_prepare_seq_read);
|
||||
EXPORT_SYMBOL(skb_seq_read);
|
||||
EXPORT_SYMBOL(skb_abort_seq_read);
|
||||
EXPORT_SYMBOL(skb_find_text);
|
||||
EXPORT_SYMBOL(skb_append_datato_frags);
|
||||
EXPORT_SYMBOL(__skb_warn_lro_forwarding);
|
||||
|
||||
EXPORT_SYMBOL_GPL(skb_to_sgvec);
|
||||
EXPORT_SYMBOL_GPL(skb_cow_data);
|
||||
EXPORT_SYMBOL_GPL(skb_partial_csum_set);
|
||||
|
||||
+76
-19
@@ -120,6 +120,7 @@
|
||||
#include <net/net_namespace.h>
|
||||
#include <net/request_sock.h>
|
||||
#include <net/sock.h>
|
||||
#include <linux/net_tstamp.h>
|
||||
#include <net/xfrm.h>
|
||||
#include <linux/ipsec.h>
|
||||
|
||||
@@ -149,7 +150,7 @@ static const char *af_family_key_strings[AF_MAX+1] = {
|
||||
"sk_lock-AF_DECnet", "sk_lock-AF_NETBEUI" , "sk_lock-AF_SECURITY" ,
|
||||
"sk_lock-AF_KEY" , "sk_lock-AF_NETLINK" , "sk_lock-AF_PACKET" ,
|
||||
"sk_lock-AF_ASH" , "sk_lock-AF_ECONET" , "sk_lock-AF_ATMSVC" ,
|
||||
"sk_lock-21" , "sk_lock-AF_SNA" , "sk_lock-AF_IRDA" ,
|
||||
"sk_lock-AF_RDS" , "sk_lock-AF_SNA" , "sk_lock-AF_IRDA" ,
|
||||
"sk_lock-AF_PPPOX" , "sk_lock-AF_WANPIPE" , "sk_lock-AF_LLC" ,
|
||||
"sk_lock-27" , "sk_lock-28" , "sk_lock-AF_CAN" ,
|
||||
"sk_lock-AF_TIPC" , "sk_lock-AF_BLUETOOTH", "sk_lock-IUCV" ,
|
||||
@@ -164,7 +165,7 @@ static const char *af_family_slock_key_strings[AF_MAX+1] = {
|
||||
"slock-AF_DECnet", "slock-AF_NETBEUI" , "slock-AF_SECURITY" ,
|
||||
"slock-AF_KEY" , "slock-AF_NETLINK" , "slock-AF_PACKET" ,
|
||||
"slock-AF_ASH" , "slock-AF_ECONET" , "slock-AF_ATMSVC" ,
|
||||
"slock-21" , "slock-AF_SNA" , "slock-AF_IRDA" ,
|
||||
"slock-AF_RDS" , "slock-AF_SNA" , "slock-AF_IRDA" ,
|
||||
"slock-AF_PPPOX" , "slock-AF_WANPIPE" , "slock-AF_LLC" ,
|
||||
"slock-27" , "slock-28" , "slock-AF_CAN" ,
|
||||
"slock-AF_TIPC" , "slock-AF_BLUETOOTH", "slock-AF_IUCV" ,
|
||||
@@ -179,7 +180,7 @@ static const char *af_family_clock_key_strings[AF_MAX+1] = {
|
||||
"clock-AF_DECnet", "clock-AF_NETBEUI" , "clock-AF_SECURITY" ,
|
||||
"clock-AF_KEY" , "clock-AF_NETLINK" , "clock-AF_PACKET" ,
|
||||
"clock-AF_ASH" , "clock-AF_ECONET" , "clock-AF_ATMSVC" ,
|
||||
"clock-21" , "clock-AF_SNA" , "clock-AF_IRDA" ,
|
||||
"clock-AF_RDS" , "clock-AF_SNA" , "clock-AF_IRDA" ,
|
||||
"clock-AF_PPPOX" , "clock-AF_WANPIPE" , "clock-AF_LLC" ,
|
||||
"clock-27" , "clock-28" , "clock-AF_CAN" ,
|
||||
"clock-AF_TIPC" , "clock-AF_BLUETOOTH", "clock-AF_IUCV" ,
|
||||
@@ -255,11 +256,14 @@ static void sock_warn_obsolete_bsdism(const char *name)
|
||||
}
|
||||
}
|
||||
|
||||
static void sock_disable_timestamp(struct sock *sk)
|
||||
static void sock_disable_timestamp(struct sock *sk, int flag)
|
||||
{
|
||||
if (sock_flag(sk, SOCK_TIMESTAMP)) {
|
||||
sock_reset_flag(sk, SOCK_TIMESTAMP);
|
||||
net_disable_timestamp();
|
||||
if (sock_flag(sk, flag)) {
|
||||
sock_reset_flag(sk, flag);
|
||||
if (!sock_flag(sk, SOCK_TIMESTAMP) &&
|
||||
!sock_flag(sk, SOCK_TIMESTAMPING_RX_SOFTWARE)) {
|
||||
net_disable_timestamp();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -614,13 +618,38 @@ set_rcvbuf:
|
||||
else
|
||||
sock_set_flag(sk, SOCK_RCVTSTAMPNS);
|
||||
sock_set_flag(sk, SOCK_RCVTSTAMP);
|
||||
sock_enable_timestamp(sk);
|
||||
sock_enable_timestamp(sk, SOCK_TIMESTAMP);
|
||||
} else {
|
||||
sock_reset_flag(sk, SOCK_RCVTSTAMP);
|
||||
sock_reset_flag(sk, SOCK_RCVTSTAMPNS);
|
||||
}
|
||||
break;
|
||||
|
||||
case SO_TIMESTAMPING:
|
||||
if (val & ~SOF_TIMESTAMPING_MASK) {
|
||||
ret = EINVAL;
|
||||
break;
|
||||
}
|
||||
sock_valbool_flag(sk, SOCK_TIMESTAMPING_TX_HARDWARE,
|
||||
val & SOF_TIMESTAMPING_TX_HARDWARE);
|
||||
sock_valbool_flag(sk, SOCK_TIMESTAMPING_TX_SOFTWARE,
|
||||
val & SOF_TIMESTAMPING_TX_SOFTWARE);
|
||||
sock_valbool_flag(sk, SOCK_TIMESTAMPING_RX_HARDWARE,
|
||||
val & SOF_TIMESTAMPING_RX_HARDWARE);
|
||||
if (val & SOF_TIMESTAMPING_RX_SOFTWARE)
|
||||
sock_enable_timestamp(sk,
|
||||
SOCK_TIMESTAMPING_RX_SOFTWARE);
|
||||
else
|
||||
sock_disable_timestamp(sk,
|
||||
SOCK_TIMESTAMPING_RX_SOFTWARE);
|
||||
sock_valbool_flag(sk, SOCK_TIMESTAMPING_SOFTWARE,
|
||||
val & SOF_TIMESTAMPING_SOFTWARE);
|
||||
sock_valbool_flag(sk, SOCK_TIMESTAMPING_SYS_HARDWARE,
|
||||
val & SOF_TIMESTAMPING_SYS_HARDWARE);
|
||||
sock_valbool_flag(sk, SOCK_TIMESTAMPING_RAW_HARDWARE,
|
||||
val & SOF_TIMESTAMPING_RAW_HARDWARE);
|
||||
break;
|
||||
|
||||
case SO_RCVLOWAT:
|
||||
if (val < 0)
|
||||
val = INT_MAX;
|
||||
@@ -768,6 +797,24 @@ int sock_getsockopt(struct socket *sock, int level, int optname,
|
||||
v.val = sock_flag(sk, SOCK_RCVTSTAMPNS);
|
||||
break;
|
||||
|
||||
case SO_TIMESTAMPING:
|
||||
v.val = 0;
|
||||
if (sock_flag(sk, SOCK_TIMESTAMPING_TX_HARDWARE))
|
||||
v.val |= SOF_TIMESTAMPING_TX_HARDWARE;
|
||||
if (sock_flag(sk, SOCK_TIMESTAMPING_TX_SOFTWARE))
|
||||
v.val |= SOF_TIMESTAMPING_TX_SOFTWARE;
|
||||
if (sock_flag(sk, SOCK_TIMESTAMPING_RX_HARDWARE))
|
||||
v.val |= SOF_TIMESTAMPING_RX_HARDWARE;
|
||||
if (sock_flag(sk, SOCK_TIMESTAMPING_RX_SOFTWARE))
|
||||
v.val |= SOF_TIMESTAMPING_RX_SOFTWARE;
|
||||
if (sock_flag(sk, SOCK_TIMESTAMPING_SOFTWARE))
|
||||
v.val |= SOF_TIMESTAMPING_SOFTWARE;
|
||||
if (sock_flag(sk, SOCK_TIMESTAMPING_SYS_HARDWARE))
|
||||
v.val |= SOF_TIMESTAMPING_SYS_HARDWARE;
|
||||
if (sock_flag(sk, SOCK_TIMESTAMPING_RAW_HARDWARE))
|
||||
v.val |= SOF_TIMESTAMPING_RAW_HARDWARE;
|
||||
break;
|
||||
|
||||
case SO_RCVTIMEO:
|
||||
lv=sizeof(struct timeval);
|
||||
if (sk->sk_rcvtimeo == MAX_SCHEDULE_TIMEOUT) {
|
||||
@@ -969,7 +1016,8 @@ void sk_free(struct sock *sk)
|
||||
rcu_assign_pointer(sk->sk_filter, NULL);
|
||||
}
|
||||
|
||||
sock_disable_timestamp(sk);
|
||||
sock_disable_timestamp(sk, SOCK_TIMESTAMP);
|
||||
sock_disable_timestamp(sk, SOCK_TIMESTAMPING_RX_SOFTWARE);
|
||||
|
||||
if (atomic_read(&sk->sk_omem_alloc))
|
||||
printk(KERN_DEBUG "%s: optmem leakage (%d bytes) detected.\n",
|
||||
@@ -1255,10 +1303,9 @@ static long sock_wait_for_wmem(struct sock * sk, long timeo)
|
||||
* Generic send/receive buffer handlers
|
||||
*/
|
||||
|
||||
static struct sk_buff *sock_alloc_send_pskb(struct sock *sk,
|
||||
unsigned long header_len,
|
||||
unsigned long data_len,
|
||||
int noblock, int *errcode)
|
||||
struct sk_buff *sock_alloc_send_pskb(struct sock *sk, unsigned long header_len,
|
||||
unsigned long data_len, int noblock,
|
||||
int *errcode)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
gfp_t gfp_mask;
|
||||
@@ -1338,6 +1385,7 @@ failure:
|
||||
*errcode = err;
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(sock_alloc_send_pskb);
|
||||
|
||||
struct sk_buff *sock_alloc_send_skb(struct sock *sk, unsigned long size,
|
||||
int noblock, int *errcode)
|
||||
@@ -1786,7 +1834,7 @@ int sock_get_timestamp(struct sock *sk, struct timeval __user *userstamp)
|
||||
{
|
||||
struct timeval tv;
|
||||
if (!sock_flag(sk, SOCK_TIMESTAMP))
|
||||
sock_enable_timestamp(sk);
|
||||
sock_enable_timestamp(sk, SOCK_TIMESTAMP);
|
||||
tv = ktime_to_timeval(sk->sk_stamp);
|
||||
if (tv.tv_sec == -1)
|
||||
return -ENOENT;
|
||||
@@ -1802,7 +1850,7 @@ int sock_get_timestampns(struct sock *sk, struct timespec __user *userstamp)
|
||||
{
|
||||
struct timespec ts;
|
||||
if (!sock_flag(sk, SOCK_TIMESTAMP))
|
||||
sock_enable_timestamp(sk);
|
||||
sock_enable_timestamp(sk, SOCK_TIMESTAMP);
|
||||
ts = ktime_to_timespec(sk->sk_stamp);
|
||||
if (ts.tv_sec == -1)
|
||||
return -ENOENT;
|
||||
@@ -1814,11 +1862,20 @@ int sock_get_timestampns(struct sock *sk, struct timespec __user *userstamp)
|
||||
}
|
||||
EXPORT_SYMBOL(sock_get_timestampns);
|
||||
|
||||
void sock_enable_timestamp(struct sock *sk)
|
||||
void sock_enable_timestamp(struct sock *sk, int flag)
|
||||
{
|
||||
if (!sock_flag(sk, SOCK_TIMESTAMP)) {
|
||||
sock_set_flag(sk, SOCK_TIMESTAMP);
|
||||
net_enable_timestamp();
|
||||
if (!sock_flag(sk, flag)) {
|
||||
sock_set_flag(sk, flag);
|
||||
/*
|
||||
* we just set one of the two flags which require net
|
||||
* time stamping, but time stamping might have been on
|
||||
* already because of the other one
|
||||
*/
|
||||
if (!sock_flag(sk,
|
||||
flag == SOCK_TIMESTAMP ?
|
||||
SOCK_TIMESTAMPING_RX_SOFTWARE :
|
||||
SOCK_TIMESTAMP))
|
||||
net_enable_timestamp();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <linux/socket.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/init.h>
|
||||
#include <net/ip.h>
|
||||
#include <net/sock.h>
|
||||
|
||||
static struct ctl_table net_core_table[] = {
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#include <linux/net.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/init.h>
|
||||
#include <net/sock.h>
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
/* We can spread an ack vector across multiple options */
|
||||
#define DCCP_MAX_ACKVEC_LEN (DCCP_SINGLE_OPT_MAXLEN * 2)
|
||||
|
||||
/* Estimated minimum average Ack Vector length - used for updating MPS */
|
||||
#define DCCPAV_MIN_OPTLEN 16
|
||||
|
||||
#define DCCP_ACKVEC_STATE_RECEIVED 0
|
||||
#define DCCP_ACKVEC_STATE_ECN_MARKED (1 << 6)
|
||||
#define DCCP_ACKVEC_STATE_NOT_RECEIVED (3 << 6)
|
||||
|
||||
+13
-13
@@ -42,9 +42,11 @@
|
||||
extern int dccp_debug;
|
||||
#define dccp_pr_debug(format, a...) DCCP_PR_DEBUG(dccp_debug, format, ##a)
|
||||
#define dccp_pr_debug_cat(format, a...) DCCP_PRINTK(dccp_debug, format, ##a)
|
||||
#define dccp_debug(fmt, a...) dccp_pr_debug_cat(KERN_DEBUG fmt, ##a)
|
||||
#else
|
||||
#define dccp_pr_debug(format, a...)
|
||||
#define dccp_pr_debug_cat(format, a...)
|
||||
#define dccp_debug(format, a...)
|
||||
#endif
|
||||
|
||||
extern struct inet_hashinfo dccp_hashinfo;
|
||||
@@ -61,11 +63,14 @@ extern void dccp_time_wait(struct sock *sk, int state, int timeo);
|
||||
* - DCCP-Reset with ACK Subheader and 4 bytes of Reset Code fields
|
||||
* Hence a safe upper bound for the maximum option length is 1020-28 = 992
|
||||
*/
|
||||
#define MAX_DCCP_SPECIFIC_HEADER (255 * sizeof(int))
|
||||
#define MAX_DCCP_SPECIFIC_HEADER (255 * sizeof(uint32_t))
|
||||
#define DCCP_MAX_PACKET_HDR 28
|
||||
#define DCCP_MAX_OPT_LEN (MAX_DCCP_SPECIFIC_HEADER - DCCP_MAX_PACKET_HDR)
|
||||
#define MAX_DCCP_HEADER (MAX_DCCP_SPECIFIC_HEADER + MAX_HEADER)
|
||||
|
||||
/* Upper bound for initial feature-negotiation overhead (padded to 32 bits) */
|
||||
#define DCCP_FEATNEG_OVERHEAD (32 * sizeof(uint32_t))
|
||||
|
||||
#define DCCP_TIMEWAIT_LEN (60 * HZ) /* how long to wait to destroy TIME-WAIT
|
||||
* state, about 60 seconds */
|
||||
|
||||
@@ -95,9 +100,6 @@ extern void dccp_time_wait(struct sock *sk, int state, int timeo);
|
||||
extern int sysctl_dccp_request_retries;
|
||||
extern int sysctl_dccp_retries1;
|
||||
extern int sysctl_dccp_retries2;
|
||||
extern int sysctl_dccp_feat_sequence_window;
|
||||
extern int sysctl_dccp_feat_rx_ccid;
|
||||
extern int sysctl_dccp_feat_tx_ccid;
|
||||
extern int sysctl_dccp_tx_qlen;
|
||||
extern int sysctl_dccp_sync_ratelimit;
|
||||
|
||||
@@ -409,23 +411,21 @@ static inline void dccp_hdr_set_ack(struct dccp_hdr_ack_bits *dhack,
|
||||
static inline void dccp_update_gsr(struct sock *sk, u64 seq)
|
||||
{
|
||||
struct dccp_sock *dp = dccp_sk(sk);
|
||||
const struct dccp_minisock *dmsk = dccp_msk(sk);
|
||||
|
||||
dp->dccps_gsr = seq;
|
||||
dccp_set_seqno(&dp->dccps_swl,
|
||||
dp->dccps_gsr + 1 - (dmsk->dccpms_sequence_window / 4));
|
||||
dccp_set_seqno(&dp->dccps_swh,
|
||||
dp->dccps_gsr + (3 * dmsk->dccpms_sequence_window) / 4);
|
||||
/* Sequence validity window depends on remote Sequence Window (7.5.1) */
|
||||
dp->dccps_swl = SUB48(ADD48(dp->dccps_gsr, 1), dp->dccps_r_seq_win / 4);
|
||||
dp->dccps_swh = ADD48(dp->dccps_gsr, (3 * dp->dccps_r_seq_win) / 4);
|
||||
}
|
||||
|
||||
static inline void dccp_update_gss(struct sock *sk, u64 seq)
|
||||
{
|
||||
struct dccp_sock *dp = dccp_sk(sk);
|
||||
|
||||
dp->dccps_awh = dp->dccps_gss = seq;
|
||||
dccp_set_seqno(&dp->dccps_awl,
|
||||
(dp->dccps_gss -
|
||||
dccp_msk(sk)->dccpms_sequence_window + 1));
|
||||
dp->dccps_gss = seq;
|
||||
/* Ack validity window depends on local Sequence Window value (7.5.1) */
|
||||
dp->dccps_awl = SUB48(ADD48(dp->dccps_gss, 1), dp->dccps_l_seq_win);
|
||||
dp->dccps_awh = dp->dccps_gss;
|
||||
}
|
||||
|
||||
static inline int dccp_ack_pending(const struct sock *sk)
|
||||
|
||||
+179
-53
@@ -25,6 +25,11 @@
|
||||
#include "ccid.h"
|
||||
#include "feat.h"
|
||||
|
||||
/* feature-specific sysctls - initialised to the defaults from RFC 4340, 6.4 */
|
||||
unsigned long sysctl_dccp_sequence_window __read_mostly = 100;
|
||||
int sysctl_dccp_rx_ccid __read_mostly = 2,
|
||||
sysctl_dccp_tx_ccid __read_mostly = 2;
|
||||
|
||||
/*
|
||||
* Feature activation handlers.
|
||||
*
|
||||
@@ -51,8 +56,17 @@ static int dccp_hdlr_ccid(struct sock *sk, u64 ccid, bool rx)
|
||||
|
||||
static int dccp_hdlr_seq_win(struct sock *sk, u64 seq_win, bool rx)
|
||||
{
|
||||
if (!rx)
|
||||
dccp_msk(sk)->dccpms_sequence_window = seq_win;
|
||||
struct dccp_sock *dp = dccp_sk(sk);
|
||||
|
||||
if (rx) {
|
||||
dp->dccps_r_seq_win = seq_win;
|
||||
/* propagate changes to update SWL/SWH */
|
||||
dccp_update_gsr(sk, dp->dccps_gsr);
|
||||
} else {
|
||||
dp->dccps_l_seq_win = seq_win;
|
||||
/* propagate changes to update AWL */
|
||||
dccp_update_gss(sk, dp->dccps_gss);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -194,6 +208,100 @@ static int dccp_feat_default_value(u8 feat_num)
|
||||
return idx < 0 ? 0 : dccp_feat_table[idx].default_value;
|
||||
}
|
||||
|
||||
/*
|
||||
* Debugging and verbose-printing section
|
||||
*/
|
||||
static const char *dccp_feat_fname(const u8 feat)
|
||||
{
|
||||
static const char *feature_names[] = {
|
||||
[DCCPF_RESERVED] = "Reserved",
|
||||
[DCCPF_CCID] = "CCID",
|
||||
[DCCPF_SHORT_SEQNOS] = "Allow Short Seqnos",
|
||||
[DCCPF_SEQUENCE_WINDOW] = "Sequence Window",
|
||||
[DCCPF_ECN_INCAPABLE] = "ECN Incapable",
|
||||
[DCCPF_ACK_RATIO] = "Ack Ratio",
|
||||
[DCCPF_SEND_ACK_VECTOR] = "Send ACK Vector",
|
||||
[DCCPF_SEND_NDP_COUNT] = "Send NDP Count",
|
||||
[DCCPF_MIN_CSUM_COVER] = "Min. Csum Coverage",
|
||||
[DCCPF_DATA_CHECKSUM] = "Send Data Checksum",
|
||||
};
|
||||
if (feat > DCCPF_DATA_CHECKSUM && feat < DCCPF_MIN_CCID_SPECIFIC)
|
||||
return feature_names[DCCPF_RESERVED];
|
||||
|
||||
if (feat == DCCPF_SEND_LEV_RATE)
|
||||
return "Send Loss Event Rate";
|
||||
if (feat >= DCCPF_MIN_CCID_SPECIFIC)
|
||||
return "CCID-specific";
|
||||
|
||||
return feature_names[feat];
|
||||
}
|
||||
|
||||
static const char *dccp_feat_sname[] = { "DEFAULT", "INITIALISING", "CHANGING",
|
||||
"UNSTABLE", "STABLE" };
|
||||
|
||||
#ifdef CONFIG_IP_DCCP_DEBUG
|
||||
static const char *dccp_feat_oname(const u8 opt)
|
||||
{
|
||||
switch (opt) {
|
||||
case DCCPO_CHANGE_L: return "Change_L";
|
||||
case DCCPO_CONFIRM_L: return "Confirm_L";
|
||||
case DCCPO_CHANGE_R: return "Change_R";
|
||||
case DCCPO_CONFIRM_R: return "Confirm_R";
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void dccp_feat_printval(u8 feat_num, dccp_feat_val const *val)
|
||||
{
|
||||
u8 i, type = dccp_feat_type(feat_num);
|
||||
|
||||
if (val == NULL || (type == FEAT_SP && val->sp.vec == NULL))
|
||||
dccp_pr_debug_cat("(NULL)");
|
||||
else if (type == FEAT_SP)
|
||||
for (i = 0; i < val->sp.len; i++)
|
||||
dccp_pr_debug_cat("%s%u", i ? " " : "", val->sp.vec[i]);
|
||||
else if (type == FEAT_NN)
|
||||
dccp_pr_debug_cat("%llu", (unsigned long long)val->nn);
|
||||
else
|
||||
dccp_pr_debug_cat("unknown type %u", type);
|
||||
}
|
||||
|
||||
static void dccp_feat_printvals(u8 feat_num, u8 *list, u8 len)
|
||||
{
|
||||
u8 type = dccp_feat_type(feat_num);
|
||||
dccp_feat_val fval = { .sp.vec = list, .sp.len = len };
|
||||
|
||||
if (type == FEAT_NN)
|
||||
fval.nn = dccp_decode_value_var(list, len);
|
||||
dccp_feat_printval(feat_num, &fval);
|
||||
}
|
||||
|
||||
static void dccp_feat_print_entry(struct dccp_feat_entry const *entry)
|
||||
{
|
||||
dccp_debug(" * %s %s = ", entry->is_local ? "local" : "remote",
|
||||
dccp_feat_fname(entry->feat_num));
|
||||
dccp_feat_printval(entry->feat_num, &entry->val);
|
||||
dccp_pr_debug_cat(", state=%s %s\n", dccp_feat_sname[entry->state],
|
||||
entry->needs_confirm ? "(Confirm pending)" : "");
|
||||
}
|
||||
|
||||
#define dccp_feat_print_opt(opt, feat, val, len, mandatory) do { \
|
||||
dccp_pr_debug("%s(%s, ", dccp_feat_oname(opt), dccp_feat_fname(feat));\
|
||||
dccp_feat_printvals(feat, val, len); \
|
||||
dccp_pr_debug_cat(") %s\n", mandatory ? "!" : ""); } while (0)
|
||||
|
||||
#define dccp_feat_print_fnlist(fn_list) { \
|
||||
const struct dccp_feat_entry *___entry; \
|
||||
\
|
||||
dccp_pr_debug("List Dump:\n"); \
|
||||
list_for_each_entry(___entry, fn_list, node) \
|
||||
dccp_feat_print_entry(___entry); \
|
||||
}
|
||||
#else /* ! CONFIG_IP_DCCP_DEBUG */
|
||||
#define dccp_feat_print_opt(opt, feat, val, len, mandatory)
|
||||
#define dccp_feat_print_fnlist(fn_list)
|
||||
#endif
|
||||
|
||||
static int __dccp_feat_activate(struct sock *sk, const int idx,
|
||||
const bool is_local, dccp_feat_val const *fval)
|
||||
{
|
||||
@@ -226,6 +334,10 @@ static int __dccp_feat_activate(struct sock *sk, const int idx,
|
||||
/* Location is RX if this is a local-RX or remote-TX feature */
|
||||
rx = (is_local == (dccp_feat_table[idx].rxtx == FEAT_AT_RX));
|
||||
|
||||
dccp_debug(" -> activating %s %s, %sval=%llu\n", rx ? "RX" : "TX",
|
||||
dccp_feat_fname(dccp_feat_table[idx].feat_num),
|
||||
fval ? "" : "default ", (unsigned long long)val);
|
||||
|
||||
return dccp_feat_table[idx].activation_hdlr(sk, val, rx);
|
||||
}
|
||||
|
||||
@@ -530,6 +642,7 @@ int dccp_feat_insert_opts(struct dccp_sock *dp, struct dccp_request_sock *dreq,
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
dccp_feat_print_opt(opt, pos->feat_num, ptr, len, 0);
|
||||
|
||||
if (dccp_insert_fn_opt(skb, opt, pos->feat_num, ptr, len, rpt))
|
||||
return -1;
|
||||
@@ -783,6 +896,7 @@ int dccp_feat_finalise_settings(struct dccp_sock *dp)
|
||||
while (i--)
|
||||
if (ccids[i] > 0 && dccp_feat_propagate_ccid(fn, ccids[i], i))
|
||||
return -1;
|
||||
dccp_feat_print_fnlist(fn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -901,6 +1015,8 @@ static u8 dccp_feat_change_recv(struct list_head *fn, u8 is_mandatory, u8 opt,
|
||||
if (len == 0 || type == FEAT_UNKNOWN) /* 6.1 and 6.6.8 */
|
||||
goto unknown_feature_or_value;
|
||||
|
||||
dccp_feat_print_opt(opt, feat, val, len, is_mandatory);
|
||||
|
||||
/*
|
||||
* Negotiation of NN features: Change R is invalid, so there is no
|
||||
* simultaneous negotiation; hence we do not look up in the list.
|
||||
@@ -1006,6 +1122,8 @@ static u8 dccp_feat_confirm_recv(struct list_head *fn, u8 is_mandatory, u8 opt,
|
||||
const bool local = (opt == DCCPO_CONFIRM_R);
|
||||
struct dccp_feat_entry *entry = dccp_feat_list_lookup(fn, feat, local);
|
||||
|
||||
dccp_feat_print_opt(opt, feat, val, len, is_mandatory);
|
||||
|
||||
if (entry == NULL) { /* nothing queued: ignore or handle error */
|
||||
if (is_mandatory && type == FEAT_UNKNOWN)
|
||||
return DCCP_RESET_CODE_MANDATORY_ERROR;
|
||||
@@ -1115,23 +1233,70 @@ int dccp_feat_parse_options(struct sock *sk, struct dccp_request_sock *dreq,
|
||||
return 0; /* ignore FN options in all other states */
|
||||
}
|
||||
|
||||
/**
|
||||
* dccp_feat_init - Seed feature negotiation with host-specific defaults
|
||||
* This initialises global defaults, depending on the value of the sysctls.
|
||||
* These can later be overridden by registering changes via setsockopt calls.
|
||||
* The last link in the chain is finalise_settings, to make sure that between
|
||||
* here and the start of actual feature negotiation no inconsistencies enter.
|
||||
*
|
||||
* All features not appearing below use either defaults or are otherwise
|
||||
* later adjusted through dccp_feat_finalise_settings().
|
||||
*/
|
||||
int dccp_feat_init(struct sock *sk)
|
||||
{
|
||||
struct dccp_sock *dp = dccp_sk(sk);
|
||||
struct dccp_minisock *dmsk = dccp_msk(sk);
|
||||
struct list_head *fn = &dccp_sk(sk)->dccps_featneg;
|
||||
u8 on = 1, off = 0;
|
||||
int rc;
|
||||
struct {
|
||||
u8 *val;
|
||||
u8 len;
|
||||
} tx, rx;
|
||||
|
||||
INIT_LIST_HEAD(&dmsk->dccpms_pending); /* XXX no longer used */
|
||||
INIT_LIST_HEAD(&dmsk->dccpms_conf); /* XXX no longer used */
|
||||
/* Non-negotiable (NN) features */
|
||||
rc = __feat_register_nn(fn, DCCPF_SEQUENCE_WINDOW, 0,
|
||||
sysctl_dccp_sequence_window);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Ack ratio */
|
||||
rc = __feat_register_nn(&dp->dccps_featneg, DCCPF_ACK_RATIO, 0,
|
||||
dp->dccps_l_ack_ratio);
|
||||
/* Server-priority (SP) features */
|
||||
|
||||
/* Advertise that short seqnos are not supported (7.6.1) */
|
||||
rc = __feat_register_sp(fn, DCCPF_SHORT_SEQNOS, true, true, &off, 1);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* RFC 4340 12.1: "If a DCCP is not ECN capable, ..." */
|
||||
rc = __feat_register_sp(fn, DCCPF_ECN_INCAPABLE, true, true, &on, 1);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/*
|
||||
* We advertise the available list of CCIDs and reorder according to
|
||||
* preferences, to avoid failure resulting from negotiating different
|
||||
* singleton values (which always leads to failure).
|
||||
* These settings can still (later) be overridden via sockopts.
|
||||
*/
|
||||
if (ccid_get_builtin_ccids(&tx.val, &tx.len) ||
|
||||
ccid_get_builtin_ccids(&rx.val, &rx.len))
|
||||
return -ENOBUFS;
|
||||
|
||||
if (!dccp_feat_prefer(sysctl_dccp_tx_ccid, tx.val, tx.len) ||
|
||||
!dccp_feat_prefer(sysctl_dccp_rx_ccid, rx.val, rx.len))
|
||||
goto free_ccid_lists;
|
||||
|
||||
rc = __feat_register_sp(fn, DCCPF_CCID, true, false, tx.val, tx.len);
|
||||
if (rc)
|
||||
goto free_ccid_lists;
|
||||
|
||||
rc = __feat_register_sp(fn, DCCPF_CCID, false, false, rx.val, rx.len);
|
||||
|
||||
free_ccid_lists:
|
||||
kfree(tx.val);
|
||||
kfree(rx.val);
|
||||
return rc;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(dccp_feat_init);
|
||||
|
||||
int dccp_feat_activate_values(struct sock *sk, struct list_head *fn_list)
|
||||
{
|
||||
struct dccp_sock *dp = dccp_sk(sk);
|
||||
@@ -1156,9 +1321,10 @@ int dccp_feat_activate_values(struct sock *sk, struct list_head *fn_list)
|
||||
goto activation_failed;
|
||||
}
|
||||
if (cur->state != FEAT_STABLE) {
|
||||
DCCP_CRIT("Negotiation of %s %u failed in state %u",
|
||||
DCCP_CRIT("Negotiation of %s %s failed in state %s",
|
||||
cur->is_local ? "local" : "remote",
|
||||
cur->feat_num, cur->state);
|
||||
dccp_feat_fname(cur->feat_num),
|
||||
dccp_feat_sname[cur->state]);
|
||||
goto activation_failed;
|
||||
}
|
||||
fvals[idx][cur->is_local] = &cur->val;
|
||||
@@ -1199,43 +1365,3 @@ activation_failed:
|
||||
dp->dccps_hc_rx_ackvec = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IP_DCCP_DEBUG
|
||||
const char *dccp_feat_typename(const u8 type)
|
||||
{
|
||||
switch(type) {
|
||||
case DCCPO_CHANGE_L: return("ChangeL");
|
||||
case DCCPO_CONFIRM_L: return("ConfirmL");
|
||||
case DCCPO_CHANGE_R: return("ChangeR");
|
||||
case DCCPO_CONFIRM_R: return("ConfirmR");
|
||||
/* the following case must not appear in feature negotation */
|
||||
default: dccp_pr_debug("unknown type %d [BUG!]\n", type);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *dccp_feat_name(const u8 feat)
|
||||
{
|
||||
static const char *feature_names[] = {
|
||||
[DCCPF_RESERVED] = "Reserved",
|
||||
[DCCPF_CCID] = "CCID",
|
||||
[DCCPF_SHORT_SEQNOS] = "Allow Short Seqnos",
|
||||
[DCCPF_SEQUENCE_WINDOW] = "Sequence Window",
|
||||
[DCCPF_ECN_INCAPABLE] = "ECN Incapable",
|
||||
[DCCPF_ACK_RATIO] = "Ack Ratio",
|
||||
[DCCPF_SEND_ACK_VECTOR] = "Send ACK Vector",
|
||||
[DCCPF_SEND_NDP_COUNT] = "Send NDP Count",
|
||||
[DCCPF_MIN_CSUM_COVER] = "Min. Csum Coverage",
|
||||
[DCCPF_DATA_CHECKSUM] = "Send Data Checksum",
|
||||
};
|
||||
if (feat > DCCPF_DATA_CHECKSUM && feat < DCCPF_MIN_CCID_SPECIFIC)
|
||||
return feature_names[DCCPF_RESERVED];
|
||||
|
||||
if (feat == DCCPF_SEND_LEV_RATE)
|
||||
return "Send Loss Event Rate";
|
||||
if (feat >= DCCPF_MIN_CCID_SPECIFIC)
|
||||
return "CCID-specific";
|
||||
|
||||
return feature_names[feat];
|
||||
}
|
||||
#endif /* CONFIG_IP_DCCP_DEBUG */
|
||||
|
||||
+8
-13
@@ -100,26 +100,21 @@ struct ccid_dependency {
|
||||
u8 val;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_IP_DCCP_DEBUG
|
||||
extern const char *dccp_feat_typename(const u8 type);
|
||||
extern const char *dccp_feat_name(const u8 feat);
|
||||
|
||||
static inline void dccp_feat_debug(const u8 type, const u8 feat, const u8 val)
|
||||
{
|
||||
dccp_pr_debug("%s(%s (%d), %d)\n", dccp_feat_typename(type),
|
||||
dccp_feat_name(feat), feat, val);
|
||||
}
|
||||
#else
|
||||
#define dccp_feat_debug(type, feat, val)
|
||||
#endif /* CONFIG_IP_DCCP_DEBUG */
|
||||
/*
|
||||
* Sysctls to seed defaults for feature negotiation
|
||||
*/
|
||||
extern unsigned long sysctl_dccp_sequence_window;
|
||||
extern int sysctl_dccp_rx_ccid;
|
||||
extern int sysctl_dccp_tx_ccid;
|
||||
|
||||
extern int dccp_feat_init(struct sock *sk);
|
||||
extern void dccp_feat_initialise_sysctls(void);
|
||||
extern int dccp_feat_register_sp(struct sock *sk, u8 feat, u8 is_local,
|
||||
u8 const *list, u8 len);
|
||||
extern int dccp_feat_register_nn(struct sock *sk, u8 feat, u64 val);
|
||||
extern int dccp_feat_parse_options(struct sock *, struct dccp_request_sock *,
|
||||
u8 mand, u8 opt, u8 feat, u8 *val, u8 len);
|
||||
extern int dccp_feat_clone_list(struct list_head const *, struct list_head *);
|
||||
extern int dccp_feat_init(struct sock *sk);
|
||||
|
||||
/*
|
||||
* Encoding variable-length options and their maximum length.
|
||||
|
||||
@@ -42,11 +42,6 @@ struct inet_timewait_death_row dccp_death_row = {
|
||||
|
||||
EXPORT_SYMBOL_GPL(dccp_death_row);
|
||||
|
||||
void dccp_minisock_init(struct dccp_minisock *dmsk)
|
||||
{
|
||||
dmsk->dccpms_sequence_window = sysctl_dccp_feat_sequence_window;
|
||||
}
|
||||
|
||||
void dccp_time_wait(struct sock *sk, int state, int timeo)
|
||||
{
|
||||
struct inet_timewait_sock *tw = NULL;
|
||||
@@ -110,7 +105,6 @@ struct sock *dccp_create_openreq_child(struct sock *sk,
|
||||
struct dccp_request_sock *dreq = dccp_rsk(req);
|
||||
struct inet_connection_sock *newicsk = inet_csk(newsk);
|
||||
struct dccp_sock *newdp = dccp_sk(newsk);
|
||||
struct dccp_minisock *newdmsk = dccp_msk(newsk);
|
||||
|
||||
newdp->dccps_role = DCCP_ROLE_SERVER;
|
||||
newdp->dccps_hc_rx_ackvec = NULL;
|
||||
@@ -128,10 +122,6 @@ struct sock *dccp_create_openreq_child(struct sock *sk,
|
||||
* Initialize S.GAR := S.ISS
|
||||
* Set S.ISR, S.GSR, S.SWL, S.SWH from packet or Init Cookies
|
||||
*/
|
||||
|
||||
/* See dccp_v4_conn_request */
|
||||
newdmsk->dccpms_sequence_window = req->rcv_wnd;
|
||||
|
||||
newdp->dccps_gar = newdp->dccps_iss = dreq->dreq_iss;
|
||||
dccp_update_gss(newsk, dreq->dreq_iss);
|
||||
|
||||
@@ -290,7 +280,6 @@ int dccp_reqsk_init(struct request_sock *req,
|
||||
inet_rsk(req)->rmt_port = dccp_hdr(skb)->dccph_sport;
|
||||
inet_rsk(req)->loc_port = dccp_hdr(skb)->dccph_dport;
|
||||
inet_rsk(req)->acked = 0;
|
||||
req->rcv_wnd = sysctl_dccp_feat_sequence_window;
|
||||
dreq->dreq_timestamp_echo = 0;
|
||||
|
||||
/* inherit feature negotiation options from listening socket */
|
||||
|
||||
@@ -23,10 +23,6 @@
|
||||
#include "dccp.h"
|
||||
#include "feat.h"
|
||||
|
||||
int sysctl_dccp_feat_sequence_window = DCCPF_INITIAL_SEQUENCE_WINDOW;
|
||||
int sysctl_dccp_feat_rx_ccid = DCCPF_INITIAL_CCID;
|
||||
int sysctl_dccp_feat_tx_ccid = DCCPF_INITIAL_CCID;
|
||||
|
||||
u64 dccp_decode_value_var(const u8 *bf, const u8 len)
|
||||
{
|
||||
u64 value = 0;
|
||||
@@ -502,10 +498,6 @@ int dccp_insert_fn_opt(struct sk_buff *skb, u8 type, u8 feat,
|
||||
*to++ = *val;
|
||||
if (len)
|
||||
memcpy(to, val, len);
|
||||
|
||||
dccp_pr_debug("%s(%s (%d), ...), length %d\n",
|
||||
dccp_feat_typename(type),
|
||||
dccp_feat_name(feat), feat, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+28
-9
@@ -161,21 +161,27 @@ unsigned int dccp_sync_mss(struct sock *sk, u32 pmtu)
|
||||
struct inet_connection_sock *icsk = inet_csk(sk);
|
||||
struct dccp_sock *dp = dccp_sk(sk);
|
||||
u32 ccmps = dccp_determine_ccmps(dp);
|
||||
int cur_mps = ccmps ? min(pmtu, ccmps) : pmtu;
|
||||
u32 cur_mps = ccmps ? min(pmtu, ccmps) : pmtu;
|
||||
|
||||
/* Account for header lengths and IPv4/v6 option overhead */
|
||||
cur_mps -= (icsk->icsk_af_ops->net_header_len + icsk->icsk_ext_hdr_len +
|
||||
sizeof(struct dccp_hdr) + sizeof(struct dccp_hdr_ext));
|
||||
|
||||
/*
|
||||
* FIXME: this should come from the CCID infrastructure, where, say,
|
||||
* TFRC will say it wants TIMESTAMPS, ELAPSED time, etc, for now lets
|
||||
* put a rough estimate for NDP + TIMESTAMP + TIMESTAMP_ECHO + ELAPSED
|
||||
* TIME + TFRC_OPT_LOSS_EVENT_RATE + TFRC_OPT_RECEIVE_RATE + padding to
|
||||
* make it a multiple of 4
|
||||
* Leave enough headroom for common DCCP header options.
|
||||
* This only considers options which may appear on DCCP-Data packets, as
|
||||
* per table 3 in RFC 4340, 5.8. When running out of space for other
|
||||
* options (eg. Ack Vector which can take up to 255 bytes), it is better
|
||||
* to schedule a separate Ack. Thus we leave headroom for the following:
|
||||
* - 1 byte for Slow Receiver (11.6)
|
||||
* - 6 bytes for Timestamp (13.1)
|
||||
* - 10 bytes for Timestamp Echo (13.3)
|
||||
* - 8 bytes for NDP count (7.7, when activated)
|
||||
* - 6 bytes for Data Checksum (9.3)
|
||||
* - %DCCPAV_MIN_OPTLEN bytes for Ack Vector size (11.4, when enabled)
|
||||
*/
|
||||
|
||||
cur_mps -= roundup(5 + 6 + 10 + 6 + 6 + 6, 4);
|
||||
cur_mps -= roundup(1 + 6 + 10 + dp->dccps_send_ndp_count * 8 + 6 +
|
||||
(dp->dccps_hc_rx_ackvec ? DCCPAV_MIN_OPTLEN : 0), 4);
|
||||
|
||||
/* And store cached results */
|
||||
icsk->icsk_pmtu_cookie = pmtu;
|
||||
@@ -270,7 +276,20 @@ void dccp_write_xmit(struct sock *sk, int block)
|
||||
const int len = skb->len;
|
||||
|
||||
if (sk->sk_state == DCCP_PARTOPEN) {
|
||||
/* See 8.1.5. Handshake Completion */
|
||||
const u32 cur_mps = dp->dccps_mss_cache - DCCP_FEATNEG_OVERHEAD;
|
||||
/*
|
||||
* See 8.1.5 - Handshake Completion.
|
||||
*
|
||||
* For robustness we resend Confirm options until the client has
|
||||
* entered OPEN. During the initial feature negotiation, the MPS
|
||||
* is smaller than usual, reduced by the Change/Confirm options.
|
||||
*/
|
||||
if (!list_empty(&dp->dccps_featneg) && len > cur_mps) {
|
||||
DCCP_WARN("Payload too large (%d) for featneg.\n", len);
|
||||
dccp_send_ack(sk);
|
||||
dccp_feat_list_purge(&dp->dccps_featneg);
|
||||
}
|
||||
|
||||
inet_csk_schedule_ack(sk);
|
||||
inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK,
|
||||
inet_csk(sk)->icsk_rto,
|
||||
|
||||
@@ -174,8 +174,6 @@ int dccp_init_sock(struct sock *sk, const __u8 ctl_sock_initialized)
|
||||
struct dccp_sock *dp = dccp_sk(sk);
|
||||
struct inet_connection_sock *icsk = inet_csk(sk);
|
||||
|
||||
dccp_minisock_init(&dp->dccps_minisock);
|
||||
|
||||
icsk->icsk_rto = DCCP_TIMEOUT_INIT;
|
||||
icsk->icsk_syn_retries = sysctl_dccp_request_retries;
|
||||
sk->sk_state = DCCP_CLOSED;
|
||||
|
||||
+30
-13
@@ -18,55 +18,72 @@
|
||||
#error This file should not be compiled without CONFIG_SYSCTL defined
|
||||
#endif
|
||||
|
||||
/* Boundary values */
|
||||
static int zero = 0,
|
||||
u8_max = 0xFF;
|
||||
static unsigned long seqw_min = 32;
|
||||
|
||||
static struct ctl_table dccp_default_table[] = {
|
||||
{
|
||||
.procname = "seq_window",
|
||||
.data = &sysctl_dccp_feat_sequence_window,
|
||||
.maxlen = sizeof(sysctl_dccp_feat_sequence_window),
|
||||
.data = &sysctl_dccp_sequence_window,
|
||||
.maxlen = sizeof(sysctl_dccp_sequence_window),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec,
|
||||
.proc_handler = proc_doulongvec_minmax,
|
||||
.extra1 = &seqw_min, /* RFC 4340, 7.5.2 */
|
||||
},
|
||||
{
|
||||
.procname = "rx_ccid",
|
||||
.data = &sysctl_dccp_feat_rx_ccid,
|
||||
.maxlen = sizeof(sysctl_dccp_feat_rx_ccid),
|
||||
.data = &sysctl_dccp_rx_ccid,
|
||||
.maxlen = sizeof(sysctl_dccp_rx_ccid),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec,
|
||||
.proc_handler = proc_dointvec_minmax,
|
||||
.extra1 = &zero,
|
||||
.extra2 = &u8_max, /* RFC 4340, 10. */
|
||||
},
|
||||
{
|
||||
.procname = "tx_ccid",
|
||||
.data = &sysctl_dccp_feat_tx_ccid,
|
||||
.maxlen = sizeof(sysctl_dccp_feat_tx_ccid),
|
||||
.data = &sysctl_dccp_tx_ccid,
|
||||
.maxlen = sizeof(sysctl_dccp_tx_ccid),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec,
|
||||
.proc_handler = proc_dointvec_minmax,
|
||||
.extra1 = &zero,
|
||||
.extra2 = &u8_max, /* RFC 4340, 10. */
|
||||
},
|
||||
{
|
||||
.procname = "request_retries",
|
||||
.data = &sysctl_dccp_request_retries,
|
||||
.maxlen = sizeof(sysctl_dccp_request_retries),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec,
|
||||
.proc_handler = proc_dointvec_minmax,
|
||||
.extra1 = &zero,
|
||||
.extra2 = &u8_max,
|
||||
},
|
||||
{
|
||||
.procname = "retries1",
|
||||
.data = &sysctl_dccp_retries1,
|
||||
.maxlen = sizeof(sysctl_dccp_retries1),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec,
|
||||
.proc_handler = proc_dointvec_minmax,
|
||||
.extra1 = &zero,
|
||||
.extra2 = &u8_max,
|
||||
},
|
||||
{
|
||||
.procname = "retries2",
|
||||
.data = &sysctl_dccp_retries2,
|
||||
.maxlen = sizeof(sysctl_dccp_retries2),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec,
|
||||
.proc_handler = proc_dointvec_minmax,
|
||||
.extra1 = &zero,
|
||||
.extra2 = &u8_max,
|
||||
},
|
||||
{
|
||||
.procname = "tx_qlen",
|
||||
.data = &sysctl_dccp_tx_qlen,
|
||||
.maxlen = sizeof(sysctl_dccp_tx_qlen),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec,
|
||||
.proc_handler = proc_dointvec_minmax,
|
||||
.extra1 = &zero,
|
||||
},
|
||||
{
|
||||
.procname = "sync_ratelimit",
|
||||
|
||||
+12
-13
@@ -1246,11 +1246,12 @@ static int dn_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
|
||||
|
||||
case TIOCINQ:
|
||||
lock_sock(sk);
|
||||
if ((skb = skb_peek(&scp->other_receive_queue)) != NULL) {
|
||||
skb = skb_peek(&scp->other_receive_queue);
|
||||
if (skb) {
|
||||
amount = skb->len;
|
||||
} else {
|
||||
struct sk_buff *skb = sk->sk_receive_queue.next;
|
||||
for(;;) {
|
||||
skb = sk->sk_receive_queue.next;
|
||||
for (;;) {
|
||||
if (skb ==
|
||||
(struct sk_buff *)&sk->sk_receive_queue)
|
||||
break;
|
||||
@@ -1579,16 +1580,16 @@ static int __dn_getsockopt(struct socket *sock, int level,int optname, char __us
|
||||
default:
|
||||
#ifdef CONFIG_NETFILTER
|
||||
{
|
||||
int val, len;
|
||||
int ret, len;
|
||||
|
||||
if(get_user(len, optlen))
|
||||
return -EFAULT;
|
||||
|
||||
val = nf_getsockopt(sk, PF_DECnet, optname,
|
||||
ret = nf_getsockopt(sk, PF_DECnet, optname,
|
||||
optval, &len);
|
||||
if (val >= 0)
|
||||
val = put_user(len, optlen);
|
||||
return val;
|
||||
if (ret >= 0)
|
||||
ret = put_user(len, optlen);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
case DSO_STREAM:
|
||||
@@ -2071,8 +2072,7 @@ static int dn_sendmsg(struct kiocb *iocb, struct socket *sock,
|
||||
}
|
||||
out:
|
||||
|
||||
if (skb)
|
||||
kfree_skb(skb);
|
||||
kfree_skb(skb);
|
||||
|
||||
release_sock(sk);
|
||||
|
||||
@@ -2112,9 +2112,8 @@ static struct notifier_block dn_dev_notifier = {
|
||||
|
||||
extern int dn_route_rcv(struct sk_buff *, struct net_device *, struct packet_type *, struct net_device *);
|
||||
|
||||
static struct packet_type dn_dix_packet_type = {
|
||||
.type = __constant_htons(ETH_P_DNA_RT),
|
||||
.dev = NULL, /* All devices */
|
||||
static struct packet_type dn_dix_packet_type __read_mostly = {
|
||||
.type = cpu_to_be16(ETH_P_DNA_RT),
|
||||
.func = dn_route_rcv,
|
||||
};
|
||||
|
||||
|
||||
+4
-2
@@ -684,7 +684,6 @@ static int dn_nl_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
|
||||
return -ENODEV;
|
||||
|
||||
if ((dn_db = dev->dn_ptr) == NULL) {
|
||||
int err;
|
||||
dn_db = dn_dev_create(dev, &err);
|
||||
if (!dn_db)
|
||||
return err;
|
||||
@@ -769,7 +768,8 @@ static void dn_ifaddr_notify(int event, struct dn_ifaddr *ifa)
|
||||
kfree_skb(skb);
|
||||
goto errout;
|
||||
}
|
||||
err = rtnl_notify(skb, &init_net, 0, RTNLGRP_DECnet_IFADDR, NULL, GFP_KERNEL);
|
||||
rtnl_notify(skb, &init_net, 0, RTNLGRP_DECnet_IFADDR, NULL, GFP_KERNEL);
|
||||
return;
|
||||
errout:
|
||||
if (err < 0)
|
||||
rtnl_set_sk_err(&init_net, RTNLGRP_DECnet_IFADDR, err);
|
||||
@@ -1322,6 +1322,7 @@ static inline int is_dn_dev(struct net_device *dev)
|
||||
}
|
||||
|
||||
static void *dn_dev_seq_start(struct seq_file *seq, loff_t *pos)
|
||||
__acquires(&dev_base_lock)
|
||||
{
|
||||
int i;
|
||||
struct net_device *dev;
|
||||
@@ -1364,6 +1365,7 @@ static void *dn_dev_seq_next(struct seq_file *seq, void *v, loff_t *pos)
|
||||
}
|
||||
|
||||
static void dn_dev_seq_stop(struct seq_file *seq, void *v)
|
||||
__releases(&dev_base_lock)
|
||||
{
|
||||
read_unlock(&dev_base_lock);
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ int decnet_dst_gc_interval = 2;
|
||||
|
||||
static struct dst_ops dn_dst_ops = {
|
||||
.family = PF_DECnet,
|
||||
.protocol = __constant_htons(ETH_P_DNA_RT),
|
||||
.protocol = cpu_to_be16(ETH_P_DNA_RT),
|
||||
.gc_thresh = 128,
|
||||
.gc = dn_dst_gc,
|
||||
.check = dn_dst_check,
|
||||
@@ -380,7 +380,6 @@ static int dn_return_short(struct sk_buff *skb)
|
||||
unsigned char *ptr;
|
||||
__le16 *src;
|
||||
__le16 *dst;
|
||||
__le16 tmp;
|
||||
|
||||
/* Add back headers */
|
||||
skb_push(skb, skb->data - skb_network_header(skb));
|
||||
@@ -399,10 +398,7 @@ static int dn_return_short(struct sk_buff *skb)
|
||||
ptr += 2;
|
||||
*ptr = 0; /* Zero hop count */
|
||||
|
||||
/* Swap source and destination */
|
||||
tmp = *src;
|
||||
*src = *dst;
|
||||
*dst = tmp;
|
||||
swap(*src, *dst);
|
||||
|
||||
skb->pkt_type = PACKET_OUTGOING;
|
||||
dn_rt_finish_output(skb, NULL, NULL);
|
||||
|
||||
@@ -375,7 +375,8 @@ static void dn_rtmsg_fib(int event, struct dn_fib_node *f, int z, u32 tb_id,
|
||||
kfree_skb(skb);
|
||||
goto errout;
|
||||
}
|
||||
err = rtnl_notify(skb, &init_net, pid, RTNLGRP_DECnet_ROUTE, nlh, GFP_KERNEL);
|
||||
rtnl_notify(skb, &init_net, pid, RTNLGRP_DECnet_ROUTE, nlh, GFP_KERNEL);
|
||||
return;
|
||||
errout:
|
||||
if (err < 0)
|
||||
rtnl_set_sk_err(&init_net, RTNLGRP_DECnet_ROUTE, err);
|
||||
|
||||
@@ -179,7 +179,7 @@ static int dn_node_address_handler(ctl_table *table, int write,
|
||||
}
|
||||
|
||||
if (write) {
|
||||
int len = (*lenp < DN_ASCBUF_LEN) ? *lenp : (DN_ASCBUF_LEN-1);
|
||||
len = (*lenp < DN_ASCBUF_LEN) ? *lenp : (DN_ASCBUF_LEN-1);
|
||||
|
||||
if (copy_from_user(addr, buffer, len))
|
||||
return -EFAULT;
|
||||
|
||||
+3
-3
@@ -41,13 +41,13 @@ config NET_DSA_MV88E6XXX_NEED_PPU
|
||||
default n
|
||||
|
||||
config NET_DSA_MV88E6131
|
||||
bool "Marvell 88E6131 ethernet switch chip support"
|
||||
bool "Marvell 88E6095/6095F/6131 ethernet switch chip support"
|
||||
select NET_DSA_MV88E6XXX
|
||||
select NET_DSA_MV88E6XXX_NEED_PPU
|
||||
select NET_DSA_TAG_DSA
|
||||
---help---
|
||||
This enables support for the Marvell 88E6131 ethernet switch
|
||||
chip.
|
||||
This enables support for the Marvell 88E6095/6095F/6131
|
||||
ethernet switch chips.
|
||||
|
||||
config NET_DSA_MV88E6123_61_65
|
||||
bool "Marvell 88E6123/6161/6165 ethernet switch chip support"
|
||||
|
||||
+109
-68
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* net/dsa/dsa.c - Hardware switch handling
|
||||
* Copyright (c) 2008 Marvell Semiconductor
|
||||
* Copyright (c) 2008-2009 Marvell Semiconductor
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -67,12 +67,13 @@ dsa_switch_probe(struct mii_bus *bus, int sw_addr, char **_name)
|
||||
|
||||
/* basic switch operations **************************************************/
|
||||
static struct dsa_switch *
|
||||
dsa_switch_setup(struct device *parent, struct dsa_platform_data *pd,
|
||||
struct mii_bus *bus, struct net_device *dev)
|
||||
dsa_switch_setup(struct dsa_switch_tree *dst, int index,
|
||||
struct device *parent, struct mii_bus *bus)
|
||||
{
|
||||
struct dsa_chip_data *pd = dst->pd->chip + index;
|
||||
struct dsa_switch_driver *drv;
|
||||
struct dsa_switch *ds;
|
||||
int ret;
|
||||
struct dsa_switch_driver *drv;
|
||||
char *name;
|
||||
int i;
|
||||
|
||||
@@ -81,11 +82,12 @@ dsa_switch_setup(struct device *parent, struct dsa_platform_data *pd,
|
||||
*/
|
||||
drv = dsa_switch_probe(bus, pd->sw_addr, &name);
|
||||
if (drv == NULL) {
|
||||
printk(KERN_ERR "%s: could not detect attached switch\n",
|
||||
dev->name);
|
||||
printk(KERN_ERR "%s[%d]: could not detect attached switch\n",
|
||||
dst->master_netdev->name, index);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
printk(KERN_INFO "%s: detected a %s switch\n", dev->name, name);
|
||||
printk(KERN_INFO "%s[%d]: detected a %s switch\n",
|
||||
dst->master_netdev->name, index, name);
|
||||
|
||||
|
||||
/*
|
||||
@@ -95,18 +97,16 @@ dsa_switch_setup(struct device *parent, struct dsa_platform_data *pd,
|
||||
if (ds == NULL)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
ds->pd = pd;
|
||||
ds->master_netdev = dev;
|
||||
ds->master_mii_bus = bus;
|
||||
|
||||
ds->dst = dst;
|
||||
ds->index = index;
|
||||
ds->pd = dst->pd->chip + index;
|
||||
ds->drv = drv;
|
||||
ds->tag_protocol = drv->tag_protocol;
|
||||
ds->master_mii_bus = bus;
|
||||
|
||||
|
||||
/*
|
||||
* Validate supplied switch configuration.
|
||||
*/
|
||||
ds->cpu_port = -1;
|
||||
for (i = 0; i < DSA_MAX_PORTS; i++) {
|
||||
char *name;
|
||||
|
||||
@@ -115,32 +115,28 @@ dsa_switch_setup(struct device *parent, struct dsa_platform_data *pd,
|
||||
continue;
|
||||
|
||||
if (!strcmp(name, "cpu")) {
|
||||
if (ds->cpu_port != -1) {
|
||||
if (dst->cpu_switch != -1) {
|
||||
printk(KERN_ERR "multiple cpu ports?!\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
ds->cpu_port = i;
|
||||
dst->cpu_switch = index;
|
||||
dst->cpu_port = i;
|
||||
} else if (!strcmp(name, "dsa")) {
|
||||
ds->dsa_port_mask |= 1 << i;
|
||||
} else {
|
||||
ds->valid_port_mask |= 1 << i;
|
||||
ds->phys_port_mask |= 1 << i;
|
||||
}
|
||||
}
|
||||
|
||||
if (ds->cpu_port == -1) {
|
||||
printk(KERN_ERR "no cpu port?!\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* If we use a tagging format that doesn't have an ethertype
|
||||
* field, make sure that all packets from this point on get
|
||||
* sent to the tag format's receive function. (Which will
|
||||
* discard received packets until we set ds->ports[] below.)
|
||||
* If the CPU connects to this switch, set the switch tree
|
||||
* tagging protocol to the preferred tagging format of this
|
||||
* switch.
|
||||
*/
|
||||
wmb();
|
||||
dev->dsa_ptr = (void *)ds;
|
||||
if (ds->dst->cpu_switch == index)
|
||||
ds->dst->tag_protocol = drv->tag_protocol;
|
||||
|
||||
|
||||
/*
|
||||
@@ -150,7 +146,7 @@ dsa_switch_setup(struct device *parent, struct dsa_platform_data *pd,
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = drv->set_addr(ds, dev->dev_addr);
|
||||
ret = drv->set_addr(ds, dst->master_netdev->dev_addr);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
@@ -169,18 +165,18 @@ dsa_switch_setup(struct device *parent, struct dsa_platform_data *pd,
|
||||
/*
|
||||
* Create network devices for physical switch ports.
|
||||
*/
|
||||
wmb();
|
||||
for (i = 0; i < DSA_MAX_PORTS; i++) {
|
||||
struct net_device *slave_dev;
|
||||
|
||||
if (!(ds->valid_port_mask & (1 << i)))
|
||||
if (!(ds->phys_port_mask & (1 << i)))
|
||||
continue;
|
||||
|
||||
slave_dev = dsa_slave_create(ds, parent, i, pd->port_names[i]);
|
||||
if (slave_dev == NULL) {
|
||||
printk(KERN_ERR "%s: can't create dsa slave "
|
||||
"device for port %d(%s)\n",
|
||||
dev->name, i, pd->port_names[i]);
|
||||
printk(KERN_ERR "%s[%d]: can't create dsa "
|
||||
"slave device for port %d(%s)\n",
|
||||
dst->master_netdev->name,
|
||||
index, i, pd->port_names[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -192,7 +188,6 @@ dsa_switch_setup(struct device *parent, struct dsa_platform_data *pd,
|
||||
out_free:
|
||||
mdiobus_free(ds->slave_mii_bus);
|
||||
out:
|
||||
dev->dsa_ptr = NULL;
|
||||
kfree(ds);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
@@ -212,35 +207,42 @@ static void dsa_switch_destroy(struct dsa_switch *ds)
|
||||
*/
|
||||
bool dsa_uses_dsa_tags(void *dsa_ptr)
|
||||
{
|
||||
struct dsa_switch *ds = dsa_ptr;
|
||||
struct dsa_switch_tree *dst = dsa_ptr;
|
||||
|
||||
return !!(ds->tag_protocol == htons(ETH_P_DSA));
|
||||
return !!(dst->tag_protocol == htons(ETH_P_DSA));
|
||||
}
|
||||
|
||||
bool dsa_uses_trailer_tags(void *dsa_ptr)
|
||||
{
|
||||
struct dsa_switch *ds = dsa_ptr;
|
||||
struct dsa_switch_tree *dst = dsa_ptr;
|
||||
|
||||
return !!(ds->tag_protocol == htons(ETH_P_TRAILER));
|
||||
return !!(dst->tag_protocol == htons(ETH_P_TRAILER));
|
||||
}
|
||||
|
||||
|
||||
/* link polling *************************************************************/
|
||||
static void dsa_link_poll_work(struct work_struct *ugly)
|
||||
{
|
||||
struct dsa_switch *ds;
|
||||
struct dsa_switch_tree *dst;
|
||||
int i;
|
||||
|
||||
ds = container_of(ugly, struct dsa_switch, link_poll_work);
|
||||
dst = container_of(ugly, struct dsa_switch_tree, link_poll_work);
|
||||
|
||||
ds->drv->poll_link(ds);
|
||||
mod_timer(&ds->link_poll_timer, round_jiffies(jiffies + HZ));
|
||||
for (i = 0; i < dst->pd->nr_chips; i++) {
|
||||
struct dsa_switch *ds = dst->ds[i];
|
||||
|
||||
if (ds != NULL && ds->drv->poll_link != NULL)
|
||||
ds->drv->poll_link(ds);
|
||||
}
|
||||
|
||||
mod_timer(&dst->link_poll_timer, round_jiffies(jiffies + HZ));
|
||||
}
|
||||
|
||||
static void dsa_link_poll_timer(unsigned long _ds)
|
||||
static void dsa_link_poll_timer(unsigned long _dst)
|
||||
{
|
||||
struct dsa_switch *ds = (void *)_ds;
|
||||
struct dsa_switch_tree *dst = (void *)_dst;
|
||||
|
||||
schedule_work(&ds->link_poll_work);
|
||||
schedule_work(&dst->link_poll_work);
|
||||
}
|
||||
|
||||
|
||||
@@ -303,18 +305,14 @@ static int dsa_probe(struct platform_device *pdev)
|
||||
static int dsa_version_printed;
|
||||
struct dsa_platform_data *pd = pdev->dev.platform_data;
|
||||
struct net_device *dev;
|
||||
struct mii_bus *bus;
|
||||
struct dsa_switch *ds;
|
||||
struct dsa_switch_tree *dst;
|
||||
int i;
|
||||
|
||||
if (!dsa_version_printed++)
|
||||
printk(KERN_NOTICE "Distributed Switch Architecture "
|
||||
"driver version %s\n", dsa_driver_version);
|
||||
|
||||
if (pd == NULL || pd->mii_bus == NULL || pd->netdev == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
bus = dev_to_mii_bus(pd->mii_bus);
|
||||
if (bus == NULL)
|
||||
if (pd == NULL || pd->netdev == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
dev = dev_to_net_device(pd->netdev);
|
||||
@@ -326,36 +324,79 @@ static int dsa_probe(struct platform_device *pdev)
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
ds = dsa_switch_setup(&pdev->dev, pd, bus, dev);
|
||||
if (IS_ERR(ds)) {
|
||||
dst = kzalloc(sizeof(*dst), GFP_KERNEL);
|
||||
if (dst == NULL) {
|
||||
dev_put(dev);
|
||||
return PTR_ERR(ds);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (ds->drv->poll_link != NULL) {
|
||||
INIT_WORK(&ds->link_poll_work, dsa_link_poll_work);
|
||||
init_timer(&ds->link_poll_timer);
|
||||
ds->link_poll_timer.data = (unsigned long)ds;
|
||||
ds->link_poll_timer.function = dsa_link_poll_timer;
|
||||
ds->link_poll_timer.expires = round_jiffies(jiffies + HZ);
|
||||
add_timer(&ds->link_poll_timer);
|
||||
platform_set_drvdata(pdev, dst);
|
||||
|
||||
dst->pd = pd;
|
||||
dst->master_netdev = dev;
|
||||
dst->cpu_switch = -1;
|
||||
dst->cpu_port = -1;
|
||||
|
||||
for (i = 0; i < pd->nr_chips; i++) {
|
||||
struct mii_bus *bus;
|
||||
struct dsa_switch *ds;
|
||||
|
||||
bus = dev_to_mii_bus(pd->chip[i].mii_bus);
|
||||
if (bus == NULL) {
|
||||
printk(KERN_ERR "%s[%d]: no mii bus found for "
|
||||
"dsa switch\n", dev->name, i);
|
||||
continue;
|
||||
}
|
||||
|
||||
ds = dsa_switch_setup(dst, i, &pdev->dev, bus);
|
||||
if (IS_ERR(ds)) {
|
||||
printk(KERN_ERR "%s[%d]: couldn't create dsa switch "
|
||||
"instance (error %ld)\n", dev->name, i,
|
||||
PTR_ERR(ds));
|
||||
continue;
|
||||
}
|
||||
|
||||
dst->ds[i] = ds;
|
||||
if (ds->drv->poll_link != NULL)
|
||||
dst->link_poll_needed = 1;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, ds);
|
||||
/*
|
||||
* If we use a tagging format that doesn't have an ethertype
|
||||
* field, make sure that all packets from this point on get
|
||||
* sent to the tag format's receive function.
|
||||
*/
|
||||
wmb();
|
||||
dev->dsa_ptr = (void *)dst;
|
||||
|
||||
if (dst->link_poll_needed) {
|
||||
INIT_WORK(&dst->link_poll_work, dsa_link_poll_work);
|
||||
init_timer(&dst->link_poll_timer);
|
||||
dst->link_poll_timer.data = (unsigned long)dst;
|
||||
dst->link_poll_timer.function = dsa_link_poll_timer;
|
||||
dst->link_poll_timer.expires = round_jiffies(jiffies + HZ);
|
||||
add_timer(&dst->link_poll_timer);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dsa_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct dsa_switch *ds = platform_get_drvdata(pdev);
|
||||
struct dsa_switch_tree *dst = platform_get_drvdata(pdev);
|
||||
int i;
|
||||
|
||||
if (ds->drv->poll_link != NULL)
|
||||
del_timer_sync(&ds->link_poll_timer);
|
||||
if (dst->link_poll_needed)
|
||||
del_timer_sync(&dst->link_poll_timer);
|
||||
|
||||
flush_scheduled_work();
|
||||
|
||||
dsa_switch_destroy(ds);
|
||||
for (i = 0; i < dst->pd->nr_chips; i++) {
|
||||
struct dsa_switch *ds = dst->ds[i];
|
||||
|
||||
if (ds != NULL)
|
||||
dsa_switch_destroy(ds);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+81
-16
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* net/dsa/dsa_priv.h - Hardware switch handling
|
||||
* Copyright (c) 2008 Marvell Semiconductor
|
||||
* Copyright (c) 2008-2009 Marvell Semiconductor
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -19,42 +19,107 @@
|
||||
|
||||
struct dsa_switch {
|
||||
/*
|
||||
* Configuration data for the platform device that owns
|
||||
* this dsa switch instance.
|
||||
* Parent switch tree, and switch index.
|
||||
*/
|
||||
struct dsa_platform_data *pd;
|
||||
struct dsa_switch_tree *dst;
|
||||
int index;
|
||||
|
||||
/*
|
||||
* References to network device and mii bus to use.
|
||||
* Configuration data for this switch.
|
||||
*/
|
||||
struct net_device *master_netdev;
|
||||
struct mii_bus *master_mii_bus;
|
||||
struct dsa_chip_data *pd;
|
||||
|
||||
/*
|
||||
* The used switch driver and frame tagging type.
|
||||
* The used switch driver.
|
||||
*/
|
||||
struct dsa_switch_driver *drv;
|
||||
__be16 tag_protocol;
|
||||
|
||||
/*
|
||||
* Reference to mii bus to use.
|
||||
*/
|
||||
struct mii_bus *master_mii_bus;
|
||||
|
||||
/*
|
||||
* Slave mii_bus and devices for the individual ports.
|
||||
*/
|
||||
int cpu_port;
|
||||
u32 valid_port_mask;
|
||||
struct mii_bus *slave_mii_bus;
|
||||
struct net_device *ports[DSA_MAX_PORTS];
|
||||
u32 dsa_port_mask;
|
||||
u32 phys_port_mask;
|
||||
struct mii_bus *slave_mii_bus;
|
||||
struct net_device *ports[DSA_MAX_PORTS];
|
||||
};
|
||||
|
||||
struct dsa_switch_tree {
|
||||
/*
|
||||
* Configuration data for the platform device that owns
|
||||
* this dsa switch tree instance.
|
||||
*/
|
||||
struct dsa_platform_data *pd;
|
||||
|
||||
/*
|
||||
* Reference to network device to use, and which tagging
|
||||
* protocol to use.
|
||||
*/
|
||||
struct net_device *master_netdev;
|
||||
__be16 tag_protocol;
|
||||
|
||||
/*
|
||||
* The switch and port to which the CPU is attached.
|
||||
*/
|
||||
s8 cpu_switch;
|
||||
s8 cpu_port;
|
||||
|
||||
/*
|
||||
* Link state polling.
|
||||
*/
|
||||
struct work_struct link_poll_work;
|
||||
struct timer_list link_poll_timer;
|
||||
int link_poll_needed;
|
||||
struct work_struct link_poll_work;
|
||||
struct timer_list link_poll_timer;
|
||||
|
||||
/*
|
||||
* Data for the individual switch chips.
|
||||
*/
|
||||
struct dsa_switch *ds[DSA_MAX_SWITCHES];
|
||||
};
|
||||
|
||||
static inline bool dsa_is_cpu_port(struct dsa_switch *ds, int p)
|
||||
{
|
||||
return !!(ds->index == ds->dst->cpu_switch && p == ds->dst->cpu_port);
|
||||
}
|
||||
|
||||
static inline u8 dsa_upstream_port(struct dsa_switch *ds)
|
||||
{
|
||||
struct dsa_switch_tree *dst = ds->dst;
|
||||
|
||||
/*
|
||||
* If this is the root switch (i.e. the switch that connects
|
||||
* to the CPU), return the cpu port number on this switch.
|
||||
* Else return the (DSA) port number that connects to the
|
||||
* switch that is one hop closer to the cpu.
|
||||
*/
|
||||
if (dst->cpu_switch == ds->index)
|
||||
return dst->cpu_port;
|
||||
else
|
||||
return ds->pd->rtable[dst->cpu_switch];
|
||||
}
|
||||
|
||||
struct dsa_slave_priv {
|
||||
/*
|
||||
* The linux network interface corresponding to this
|
||||
* switch port.
|
||||
*/
|
||||
struct net_device *dev;
|
||||
|
||||
/*
|
||||
* Which switch this port is a part of, and the port index
|
||||
* for this port.
|
||||
*/
|
||||
struct dsa_switch *parent;
|
||||
int port;
|
||||
u8 port;
|
||||
|
||||
/*
|
||||
* The phylib phy_device pointer for the PHY connected
|
||||
* to this port.
|
||||
*/
|
||||
struct phy_device *phy;
|
||||
};
|
||||
|
||||
|
||||
+6
-6
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* net/dsa/mv88e6060.c - Driver for Marvell 88e6060 switch chips
|
||||
* Copyright (c) 2008 Marvell Semiconductor
|
||||
* Copyright (c) 2008-2009 Marvell Semiconductor
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -81,7 +81,7 @@ static int mv88e6060_switch_reset(struct dsa_switch *ds)
|
||||
/*
|
||||
* Reset the switch.
|
||||
*/
|
||||
REG_WRITE(REG_GLOBAL, 0x0A, 0xa130);
|
||||
REG_WRITE(REG_GLOBAL, 0x0a, 0xa130);
|
||||
|
||||
/*
|
||||
* Wait up to one second for reset to complete.
|
||||
@@ -128,7 +128,7 @@ static int mv88e6060_setup_port(struct dsa_switch *ds, int p)
|
||||
* state to Forwarding. Additionally, if this is the CPU
|
||||
* port, enable Ingress and Egress Trailer tagging mode.
|
||||
*/
|
||||
REG_WRITE(addr, 0x04, (p == ds->cpu_port) ? 0x4103 : 0x0003);
|
||||
REG_WRITE(addr, 0x04, dsa_is_cpu_port(ds, p) ? 0x4103 : 0x0003);
|
||||
|
||||
/*
|
||||
* Port based VLAN map: give each port its own address
|
||||
@@ -138,9 +138,9 @@ static int mv88e6060_setup_port(struct dsa_switch *ds, int p)
|
||||
*/
|
||||
REG_WRITE(addr, 0x06,
|
||||
((p & 0xf) << 12) |
|
||||
((p == ds->cpu_port) ?
|
||||
ds->valid_port_mask :
|
||||
(1 << ds->cpu_port)));
|
||||
(dsa_is_cpu_port(ds, p) ?
|
||||
ds->phys_port_mask :
|
||||
(1 << ds->dst->cpu_port)));
|
||||
|
||||
/*
|
||||
* Port Association Vector: when learning source addresses
|
||||
|
||||
+58
-32
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* net/dsa/mv88e6123_61_65.c - Marvell 88e6123/6161/6165 switch chip support
|
||||
* Copyright (c) 2008 Marvell Semiconductor
|
||||
* Copyright (c) 2008-2009 Marvell Semiconductor
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -98,17 +98,17 @@ static int mv88e6123_61_65_setup_global(struct dsa_switch *ds)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Configure the cpu port, and configure the cpu port as the
|
||||
* port to which ingress and egress monitor frames are to be
|
||||
* sent.
|
||||
* Configure the upstream port, and configure the upstream
|
||||
* port as the port to which ingress and egress monitor frames
|
||||
* are to be sent.
|
||||
*/
|
||||
REG_WRITE(REG_GLOBAL, 0x1a, (ds->cpu_port * 0x1110));
|
||||
REG_WRITE(REG_GLOBAL, 0x1a, (dsa_upstream_port(ds) * 0x1110));
|
||||
|
||||
/*
|
||||
* Disable remote management for now, and set the switch's
|
||||
* DSA device number to zero.
|
||||
* DSA device number.
|
||||
*/
|
||||
REG_WRITE(REG_GLOBAL, 0x1c, 0x0000);
|
||||
REG_WRITE(REG_GLOBAL, 0x1c, ds->index & 0x1f);
|
||||
|
||||
/*
|
||||
* Send all frames with destination addresses matching
|
||||
@@ -133,10 +133,17 @@ static int mv88e6123_61_65_setup_global(struct dsa_switch *ds)
|
||||
REG_WRITE(REG_GLOBAL2, 0x05, 0x00ff);
|
||||
|
||||
/*
|
||||
* Map all DSA device IDs to the CPU port.
|
||||
* Program the DSA routing table.
|
||||
*/
|
||||
for (i = 0; i < 32; i++)
|
||||
REG_WRITE(REG_GLOBAL2, 0x06, 0x8000 | (i << 8) | ds->cpu_port);
|
||||
for (i = 0; i < 32; i++) {
|
||||
int nexthop;
|
||||
|
||||
nexthop = 0x1f;
|
||||
if (i != ds->index && i < ds->dst->pd->nr_chips)
|
||||
nexthop = ds->pd->rtable[i] & 0x1f;
|
||||
|
||||
REG_WRITE(REG_GLOBAL2, 0x06, 0x8000 | (i << 8) | nexthop);
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear all trunk masks.
|
||||
@@ -176,12 +183,18 @@ static int mv88e6123_61_65_setup_global(struct dsa_switch *ds)
|
||||
static int mv88e6123_61_65_setup_port(struct dsa_switch *ds, int p)
|
||||
{
|
||||
int addr = REG_PORT(p);
|
||||
u16 val;
|
||||
|
||||
/*
|
||||
* MAC Forcing register: don't force link, speed, duplex
|
||||
* or flow control state to any particular values.
|
||||
* or flow control state to any particular values on physical
|
||||
* ports, but force the CPU port and all DSA ports to 1000 Mb/s
|
||||
* full duplex.
|
||||
*/
|
||||
REG_WRITE(addr, 0x01, 0x0003);
|
||||
if (dsa_is_cpu_port(ds, p) || ds->dsa_port_mask & (1 << p))
|
||||
REG_WRITE(addr, 0x01, 0x003e);
|
||||
else
|
||||
REG_WRITE(addr, 0x01, 0x0003);
|
||||
|
||||
/*
|
||||
* Do not limit the period of time that this port can be
|
||||
@@ -192,37 +205,50 @@ static int mv88e6123_61_65_setup_port(struct dsa_switch *ds, int p)
|
||||
|
||||
/*
|
||||
* Port Control: disable Drop-on-Unlock, disable Drop-on-Lock,
|
||||
* configure the requested (DSA/EDSA) tagging mode if this is
|
||||
* the CPU port, disable Header mode, enable IGMP/MLD snooping,
|
||||
* disable VLAN tunneling, determine priority by looking at
|
||||
* 802.1p and IP priority fields (IP prio has precedence), and
|
||||
* set STP state to Forwarding. Finally, if this is the CPU
|
||||
* port, additionally enable forwarding of unknown unicast and
|
||||
* multicast addresses.
|
||||
* disable Header mode, enable IGMP/MLD snooping, disable VLAN
|
||||
* tunneling, determine priority by looking at 802.1p and IP
|
||||
* priority fields (IP prio has precedence), and set STP state
|
||||
* to Forwarding.
|
||||
*
|
||||
* If this is the CPU link, use DSA or EDSA tagging depending
|
||||
* on which tagging mode was configured.
|
||||
*
|
||||
* If this is a link to another switch, use DSA tagging mode.
|
||||
*
|
||||
* If this is the upstream port for this switch, enable
|
||||
* forwarding of unknown unicasts and multicasts.
|
||||
*/
|
||||
REG_WRITE(addr, 0x04,
|
||||
(p == ds->cpu_port) ?
|
||||
(ds->tag_protocol == htons(ETH_P_DSA)) ?
|
||||
0x053f : 0x373f :
|
||||
0x0433);
|
||||
val = 0x0433;
|
||||
if (dsa_is_cpu_port(ds, p)) {
|
||||
if (ds->dst->tag_protocol == htons(ETH_P_EDSA))
|
||||
val |= 0x3300;
|
||||
else
|
||||
val |= 0x0100;
|
||||
}
|
||||
if (ds->dsa_port_mask & (1 << p))
|
||||
val |= 0x0100;
|
||||
if (p == dsa_upstream_port(ds))
|
||||
val |= 0x000c;
|
||||
REG_WRITE(addr, 0x04, val);
|
||||
|
||||
/*
|
||||
* Port Control 1: disable trunking. Also, if this is the
|
||||
* CPU port, enable learn messages to be sent to this port.
|
||||
*/
|
||||
REG_WRITE(addr, 0x05, (p == ds->cpu_port) ? 0x8000 : 0x0000);
|
||||
REG_WRITE(addr, 0x05, dsa_is_cpu_port(ds, p) ? 0x8000 : 0x0000);
|
||||
|
||||
/*
|
||||
* Port based VLAN map: give each port its own address
|
||||
* database, allow the CPU port to talk to each of the 'real'
|
||||
* ports, and allow each of the 'real' ports to only talk to
|
||||
* the CPU port.
|
||||
* the upstream port.
|
||||
*/
|
||||
REG_WRITE(addr, 0x06,
|
||||
((p & 0xf) << 12) |
|
||||
((p == ds->cpu_port) ?
|
||||
ds->valid_port_mask :
|
||||
(1 << ds->cpu_port)));
|
||||
val = (p & 0xf) << 12;
|
||||
if (dsa_is_cpu_port(ds, p))
|
||||
val |= ds->phys_port_mask;
|
||||
else
|
||||
val |= 1 << dsa_upstream_port(ds);
|
||||
REG_WRITE(addr, 0x06, val);
|
||||
|
||||
/*
|
||||
* Default VLAN ID and priority: don't set a default VLAN
|
||||
@@ -394,7 +420,7 @@ static int mv88e6123_61_65_get_sset_count(struct dsa_switch *ds)
|
||||
}
|
||||
|
||||
static struct dsa_switch_driver mv88e6123_61_65_switch_driver = {
|
||||
.tag_protocol = __constant_htons(ETH_P_EDSA),
|
||||
.tag_protocol = cpu_to_be16(ETH_P_EDSA),
|
||||
.priv_size = sizeof(struct mv88e6xxx_priv_state),
|
||||
.probe = mv88e6123_61_65_probe,
|
||||
.setup = mv88e6123_61_65_setup,
|
||||
|
||||
+63
-35
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* net/dsa/mv88e6131.c - Marvell 88e6131 switch chip support
|
||||
* Copyright (c) 2008 Marvell Semiconductor
|
||||
* net/dsa/mv88e6131.c - Marvell 88e6095/6095f/6131 switch chip support
|
||||
* Copyright (c) 2008-2009 Marvell Semiconductor
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -21,6 +21,8 @@ static char *mv88e6131_probe(struct mii_bus *bus, int sw_addr)
|
||||
ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), 0x03);
|
||||
if (ret >= 0) {
|
||||
ret &= 0xfff0;
|
||||
if (ret == 0x0950)
|
||||
return "Marvell 88E6095/88E6095F";
|
||||
if (ret == 0x1060)
|
||||
return "Marvell 88E6131";
|
||||
}
|
||||
@@ -36,7 +38,7 @@ static int mv88e6131_switch_reset(struct dsa_switch *ds)
|
||||
/*
|
||||
* Set all ports to the disabled state.
|
||||
*/
|
||||
for (i = 0; i < 8; i++) {
|
||||
for (i = 0; i < 11; i++) {
|
||||
ret = REG_READ(REG_PORT(i), 0x04);
|
||||
REG_WRITE(REG_PORT(i), 0x04, ret & 0xfffc);
|
||||
}
|
||||
@@ -100,17 +102,17 @@ static int mv88e6131_setup_global(struct dsa_switch *ds)
|
||||
REG_WRITE(REG_GLOBAL, 0x19, 0x8100);
|
||||
|
||||
/*
|
||||
* Disable ARP mirroring, and configure the cpu port as the
|
||||
* port to which ingress and egress monitor frames are to be
|
||||
* sent.
|
||||
* Disable ARP mirroring, and configure the upstream port as
|
||||
* the port to which ingress and egress monitor frames are to
|
||||
* be sent.
|
||||
*/
|
||||
REG_WRITE(REG_GLOBAL, 0x1a, (ds->cpu_port * 0x1100) | 0x00f0);
|
||||
REG_WRITE(REG_GLOBAL, 0x1a, (dsa_upstream_port(ds) * 0x1100) | 0x00f0);
|
||||
|
||||
/*
|
||||
* Disable cascade port functionality, and set the switch's
|
||||
* DSA device number to zero.
|
||||
* DSA device number.
|
||||
*/
|
||||
REG_WRITE(REG_GLOBAL, 0x1c, 0xe000);
|
||||
REG_WRITE(REG_GLOBAL, 0x1c, 0xe000 | (ds->index & 0x1f));
|
||||
|
||||
/*
|
||||
* Send all frames with destination addresses matching
|
||||
@@ -127,16 +129,23 @@ static int mv88e6131_setup_global(struct dsa_switch *ds)
|
||||
REG_WRITE(REG_GLOBAL2, 0x05, 0x00ff);
|
||||
|
||||
/*
|
||||
* Map all DSA device IDs to the CPU port.
|
||||
* Program the DSA routing table.
|
||||
*/
|
||||
for (i = 0; i < 32; i++)
|
||||
REG_WRITE(REG_GLOBAL2, 0x06, 0x8000 | (i << 8) | ds->cpu_port);
|
||||
for (i = 0; i < 32; i++) {
|
||||
int nexthop;
|
||||
|
||||
nexthop = 0x1f;
|
||||
if (i != ds->index && i < ds->dst->pd->nr_chips)
|
||||
nexthop = ds->pd->rtable[i] & 0x1f;
|
||||
|
||||
REG_WRITE(REG_GLOBAL2, 0x06, 0x8000 | (i << 8) | nexthop);
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear all trunk masks.
|
||||
*/
|
||||
for (i = 0; i < 8; i++)
|
||||
REG_WRITE(REG_GLOBAL2, 0x07, 0x8000 | (i << 12) | 0xff);
|
||||
REG_WRITE(REG_GLOBAL2, 0x07, 0x8000 | (i << 12) | 0x7ff);
|
||||
|
||||
/*
|
||||
* Clear all trunk mappings.
|
||||
@@ -156,12 +165,18 @@ static int mv88e6131_setup_global(struct dsa_switch *ds)
|
||||
static int mv88e6131_setup_port(struct dsa_switch *ds, int p)
|
||||
{
|
||||
int addr = REG_PORT(p);
|
||||
u16 val;
|
||||
|
||||
/*
|
||||
* MAC Forcing register: don't force link, speed, duplex
|
||||
* or flow control state to any particular values.
|
||||
* or flow control state to any particular values on physical
|
||||
* ports, but force the CPU port and all DSA ports to 1000 Mb/s
|
||||
* full duplex.
|
||||
*/
|
||||
REG_WRITE(addr, 0x01, 0x0003);
|
||||
if (dsa_is_cpu_port(ds, p) || ds->dsa_port_mask & (1 << p))
|
||||
REG_WRITE(addr, 0x01, 0x003e);
|
||||
else
|
||||
REG_WRITE(addr, 0x01, 0x0003);
|
||||
|
||||
/*
|
||||
* Port Control: disable Core Tag, disable Drop-on-Lock,
|
||||
@@ -169,29 +184,40 @@ static int mv88e6131_setup_port(struct dsa_switch *ds, int p)
|
||||
* enable IGMP/MLD snoop, disable DoubleTag, disable VLAN
|
||||
* tunneling, determine priority by looking at 802.1p and
|
||||
* IP priority fields (IP prio has precedence), and set STP
|
||||
* state to Forwarding. Finally, if this is the CPU port,
|
||||
* additionally enable DSA tagging and forwarding of unknown
|
||||
* unicast addresses.
|
||||
* state to Forwarding.
|
||||
*
|
||||
* If this is the upstream port for this switch, enable
|
||||
* forwarding of unknown unicasts, and enable DSA tagging
|
||||
* mode.
|
||||
*
|
||||
* If this is the link to another switch, use DSA tagging
|
||||
* mode, but do not enable forwarding of unknown unicasts.
|
||||
*/
|
||||
REG_WRITE(addr, 0x04, (p == ds->cpu_port) ? 0x0537 : 0x0433);
|
||||
val = 0x0433;
|
||||
if (p == dsa_upstream_port(ds))
|
||||
val |= 0x0104;
|
||||
if (ds->dsa_port_mask & (1 << p))
|
||||
val |= 0x0100;
|
||||
REG_WRITE(addr, 0x04, val);
|
||||
|
||||
/*
|
||||
* Port Control 1: disable trunking. Also, if this is the
|
||||
* CPU port, enable learn messages to be sent to this port.
|
||||
*/
|
||||
REG_WRITE(addr, 0x05, (p == ds->cpu_port) ? 0x8000 : 0x0000);
|
||||
REG_WRITE(addr, 0x05, dsa_is_cpu_port(ds, p) ? 0x8000 : 0x0000);
|
||||
|
||||
/*
|
||||
* Port based VLAN map: give each port its own address
|
||||
* database, allow the CPU port to talk to each of the 'real'
|
||||
* ports, and allow each of the 'real' ports to only talk to
|
||||
* the CPU port.
|
||||
* the upstream port.
|
||||
*/
|
||||
REG_WRITE(addr, 0x06,
|
||||
((p & 0xf) << 12) |
|
||||
((p == ds->cpu_port) ?
|
||||
ds->valid_port_mask :
|
||||
(1 << ds->cpu_port)));
|
||||
val = (p & 0xf) << 12;
|
||||
if (dsa_is_cpu_port(ds, p))
|
||||
val |= ds->phys_port_mask;
|
||||
else
|
||||
val |= 1 << dsa_upstream_port(ds);
|
||||
REG_WRITE(addr, 0x06, val);
|
||||
|
||||
/*
|
||||
* Default VLAN ID and priority: don't set a default VLAN
|
||||
@@ -207,13 +233,15 @@ static int mv88e6131_setup_port(struct dsa_switch *ds, int p)
|
||||
* untagged frames on this port, do a destination address
|
||||
* lookup on received packets as usual, don't send a copy
|
||||
* of all transmitted/received frames on this port to the
|
||||
* CPU, and configure the CPU port number. Also, if this
|
||||
* is the CPU port, enable forwarding of unknown multicast
|
||||
* addresses.
|
||||
* CPU, and configure the upstream port number.
|
||||
*
|
||||
* If this is the upstream port for this switch, enable
|
||||
* forwarding of unknown multicast addresses.
|
||||
*/
|
||||
REG_WRITE(addr, 0x08,
|
||||
((p == ds->cpu_port) ? 0x00c0 : 0x0080) |
|
||||
ds->cpu_port);
|
||||
val = 0x0080 | dsa_upstream_port(ds);
|
||||
if (p == dsa_upstream_port(ds))
|
||||
val |= 0x0040;
|
||||
REG_WRITE(addr, 0x08, val);
|
||||
|
||||
/*
|
||||
* Rate Control: disable ingress rate limiting.
|
||||
@@ -268,7 +296,7 @@ static int mv88e6131_setup(struct dsa_switch *ds)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < 6; i++) {
|
||||
for (i = 0; i < 11; i++) {
|
||||
ret = mv88e6131_setup_port(ds, i);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
@@ -279,7 +307,7 @@ static int mv88e6131_setup(struct dsa_switch *ds)
|
||||
|
||||
static int mv88e6131_port_to_phy_addr(int port)
|
||||
{
|
||||
if (port >= 0 && port != 3 && port <= 7)
|
||||
if (port >= 0 && port <= 11)
|
||||
return port;
|
||||
return -1;
|
||||
}
|
||||
@@ -353,7 +381,7 @@ static int mv88e6131_get_sset_count(struct dsa_switch *ds)
|
||||
}
|
||||
|
||||
static struct dsa_switch_driver mv88e6131_switch_driver = {
|
||||
.tag_protocol = __constant_htons(ETH_P_DSA),
|
||||
.tag_protocol = cpu_to_be16(ETH_P_DSA),
|
||||
.priv_size = sizeof(struct mv88e6xxx_priv_state),
|
||||
.probe = mv88e6131_probe,
|
||||
.setup = mv88e6131_setup,
|
||||
|
||||
+23
-11
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* net/dsa/slave.c - Slave device handling
|
||||
* Copyright (c) 2008 Marvell Semiconductor
|
||||
* Copyright (c) 2008-2009 Marvell Semiconductor
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -19,7 +19,7 @@ static int dsa_slave_phy_read(struct mii_bus *bus, int addr, int reg)
|
||||
{
|
||||
struct dsa_switch *ds = bus->priv;
|
||||
|
||||
if (ds->valid_port_mask & (1 << addr))
|
||||
if (ds->phys_port_mask & (1 << addr))
|
||||
return ds->drv->phy_read(ds, addr, reg);
|
||||
|
||||
return 0xffff;
|
||||
@@ -29,7 +29,7 @@ static int dsa_slave_phy_write(struct mii_bus *bus, int addr, int reg, u16 val)
|
||||
{
|
||||
struct dsa_switch *ds = bus->priv;
|
||||
|
||||
if (ds->valid_port_mask & (1 << addr))
|
||||
if (ds->phys_port_mask & (1 << addr))
|
||||
return ds->drv->phy_write(ds, addr, reg, val);
|
||||
|
||||
return 0;
|
||||
@@ -43,15 +43,24 @@ void dsa_slave_mii_bus_init(struct dsa_switch *ds)
|
||||
ds->slave_mii_bus->write = dsa_slave_phy_write;
|
||||
snprintf(ds->slave_mii_bus->id, MII_BUS_ID_SIZE, "%s:%.2x",
|
||||
ds->master_mii_bus->id, ds->pd->sw_addr);
|
||||
ds->slave_mii_bus->parent = &(ds->master_mii_bus->dev);
|
||||
ds->slave_mii_bus->parent = &ds->master_mii_bus->dev;
|
||||
}
|
||||
|
||||
|
||||
/* slave device handling ****************************************************/
|
||||
static int dsa_slave_init(struct net_device *dev)
|
||||
{
|
||||
struct dsa_slave_priv *p = netdev_priv(dev);
|
||||
|
||||
dev->iflink = p->parent->dst->master_netdev->ifindex;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dsa_slave_open(struct net_device *dev)
|
||||
{
|
||||
struct dsa_slave_priv *p = netdev_priv(dev);
|
||||
struct net_device *master = p->parent->master_netdev;
|
||||
struct net_device *master = p->parent->dst->master_netdev;
|
||||
int err;
|
||||
|
||||
if (!(master->flags & IFF_UP))
|
||||
@@ -89,7 +98,7 @@ out:
|
||||
static int dsa_slave_close(struct net_device *dev)
|
||||
{
|
||||
struct dsa_slave_priv *p = netdev_priv(dev);
|
||||
struct net_device *master = p->parent->master_netdev;
|
||||
struct net_device *master = p->parent->dst->master_netdev;
|
||||
|
||||
dev_mc_unsync(master, dev);
|
||||
dev_unicast_unsync(master, dev);
|
||||
@@ -107,7 +116,7 @@ static int dsa_slave_close(struct net_device *dev)
|
||||
static void dsa_slave_change_rx_flags(struct net_device *dev, int change)
|
||||
{
|
||||
struct dsa_slave_priv *p = netdev_priv(dev);
|
||||
struct net_device *master = p->parent->master_netdev;
|
||||
struct net_device *master = p->parent->dst->master_netdev;
|
||||
|
||||
if (change & IFF_ALLMULTI)
|
||||
dev_set_allmulti(master, dev->flags & IFF_ALLMULTI ? 1 : -1);
|
||||
@@ -118,7 +127,7 @@ static void dsa_slave_change_rx_flags(struct net_device *dev, int change)
|
||||
static void dsa_slave_set_rx_mode(struct net_device *dev)
|
||||
{
|
||||
struct dsa_slave_priv *p = netdev_priv(dev);
|
||||
struct net_device *master = p->parent->master_netdev;
|
||||
struct net_device *master = p->parent->dst->master_netdev;
|
||||
|
||||
dev_mc_sync(master, dev);
|
||||
dev_unicast_sync(master, dev);
|
||||
@@ -127,7 +136,7 @@ static void dsa_slave_set_rx_mode(struct net_device *dev)
|
||||
static int dsa_slave_set_mac_address(struct net_device *dev, void *a)
|
||||
{
|
||||
struct dsa_slave_priv *p = netdev_priv(dev);
|
||||
struct net_device *master = p->parent->master_netdev;
|
||||
struct net_device *master = p->parent->dst->master_netdev;
|
||||
struct sockaddr *addr = a;
|
||||
int err;
|
||||
|
||||
@@ -288,6 +297,7 @@ static const struct ethtool_ops dsa_slave_ethtool_ops = {
|
||||
|
||||
#ifdef CONFIG_NET_DSA_TAG_DSA
|
||||
static const struct net_device_ops dsa_netdev_ops = {
|
||||
.ndo_init = dsa_slave_init,
|
||||
.ndo_open = dsa_slave_open,
|
||||
.ndo_stop = dsa_slave_close,
|
||||
.ndo_start_xmit = dsa_xmit,
|
||||
@@ -300,6 +310,7 @@ static const struct net_device_ops dsa_netdev_ops = {
|
||||
#endif
|
||||
#ifdef CONFIG_NET_DSA_TAG_EDSA
|
||||
static const struct net_device_ops edsa_netdev_ops = {
|
||||
.ndo_init = dsa_slave_init,
|
||||
.ndo_open = dsa_slave_open,
|
||||
.ndo_stop = dsa_slave_close,
|
||||
.ndo_start_xmit = edsa_xmit,
|
||||
@@ -312,6 +323,7 @@ static const struct net_device_ops edsa_netdev_ops = {
|
||||
#endif
|
||||
#ifdef CONFIG_NET_DSA_TAG_TRAILER
|
||||
static const struct net_device_ops trailer_netdev_ops = {
|
||||
.ndo_init = dsa_slave_init,
|
||||
.ndo_open = dsa_slave_open,
|
||||
.ndo_stop = dsa_slave_close,
|
||||
.ndo_start_xmit = trailer_xmit,
|
||||
@@ -328,7 +340,7 @@ struct net_device *
|
||||
dsa_slave_create(struct dsa_switch *ds, struct device *parent,
|
||||
int port, char *name)
|
||||
{
|
||||
struct net_device *master = ds->master_netdev;
|
||||
struct net_device *master = ds->dst->master_netdev;
|
||||
struct net_device *slave_dev;
|
||||
struct dsa_slave_priv *p;
|
||||
int ret;
|
||||
@@ -343,7 +355,7 @@ dsa_slave_create(struct dsa_switch *ds, struct device *parent,
|
||||
memcpy(slave_dev->dev_addr, master->dev_addr, ETH_ALEN);
|
||||
slave_dev->tx_queue_len = 0;
|
||||
|
||||
switch (ds->tag_protocol) {
|
||||
switch (ds->dst->tag_protocol) {
|
||||
#ifdef CONFIG_NET_DSA_TAG_DSA
|
||||
case htons(ETH_P_DSA):
|
||||
slave_dev->netdev_ops = &dsa_netdev_ops;
|
||||
|
||||
+22
-12
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* net/dsa/tag_dsa.c - (Non-ethertype) DSA tagging
|
||||
* Copyright (c) 2008 Marvell Semiconductor
|
||||
* Copyright (c) 2008-2009 Marvell Semiconductor
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -36,7 +36,7 @@ int dsa_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
* Construct tagged FROM_CPU DSA tag from 802.1q tag.
|
||||
*/
|
||||
dsa_header = skb->data + 2 * ETH_ALEN;
|
||||
dsa_header[0] = 0x60;
|
||||
dsa_header[0] = 0x60 | p->parent->index;
|
||||
dsa_header[1] = p->port << 3;
|
||||
|
||||
/*
|
||||
@@ -57,7 +57,7 @@ int dsa_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
* Construct untagged FROM_CPU DSA tag.
|
||||
*/
|
||||
dsa_header = skb->data + 2 * ETH_ALEN;
|
||||
dsa_header[0] = 0x40;
|
||||
dsa_header[0] = 0x40 | p->parent->index;
|
||||
dsa_header[1] = p->port << 3;
|
||||
dsa_header[2] = 0x00;
|
||||
dsa_header[3] = 0x00;
|
||||
@@ -65,7 +65,7 @@ int dsa_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
|
||||
skb->protocol = htons(ETH_P_DSA);
|
||||
|
||||
skb->dev = p->parent->master_netdev;
|
||||
skb->dev = p->parent->dst->master_netdev;
|
||||
dev_queue_xmit(skb);
|
||||
|
||||
return NETDEV_TX_OK;
|
||||
@@ -78,11 +78,13 @@ out_free:
|
||||
static int dsa_rcv(struct sk_buff *skb, struct net_device *dev,
|
||||
struct packet_type *pt, struct net_device *orig_dev)
|
||||
{
|
||||
struct dsa_switch *ds = dev->dsa_ptr;
|
||||
struct dsa_switch_tree *dst = dev->dsa_ptr;
|
||||
struct dsa_switch *ds;
|
||||
u8 *dsa_header;
|
||||
int source_device;
|
||||
int source_port;
|
||||
|
||||
if (unlikely(ds == NULL))
|
||||
if (unlikely(dst == NULL))
|
||||
goto out_drop;
|
||||
|
||||
skb = skb_unshare(skb, GFP_ATOMIC);
|
||||
@@ -98,16 +100,24 @@ static int dsa_rcv(struct sk_buff *skb, struct net_device *dev,
|
||||
dsa_header = skb->data - 2;
|
||||
|
||||
/*
|
||||
* Check that frame type is either TO_CPU or FORWARD, and
|
||||
* that the source device is zero.
|
||||
* Check that frame type is either TO_CPU or FORWARD.
|
||||
*/
|
||||
if ((dsa_header[0] & 0xdf) != 0x00 && (dsa_header[0] & 0xdf) != 0xc0)
|
||||
if ((dsa_header[0] & 0xc0) != 0x00 && (dsa_header[0] & 0xc0) != 0xc0)
|
||||
goto out_drop;
|
||||
|
||||
/*
|
||||
* Check that the source port is a registered DSA port.
|
||||
* Determine source device and port.
|
||||
*/
|
||||
source_device = dsa_header[0] & 0x1f;
|
||||
source_port = (dsa_header[1] >> 3) & 0x1f;
|
||||
|
||||
/*
|
||||
* Check that the source device exists and that the source
|
||||
* port is a registered DSA port.
|
||||
*/
|
||||
if (source_device >= dst->pd->nr_chips)
|
||||
goto out_drop;
|
||||
ds = dst->ds[source_device];
|
||||
if (source_port >= DSA_MAX_PORTS || ds->ports[source_port] == NULL)
|
||||
goto out_drop;
|
||||
|
||||
@@ -175,8 +185,8 @@ out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct packet_type dsa_packet_type = {
|
||||
.type = __constant_htons(ETH_P_DSA),
|
||||
static struct packet_type dsa_packet_type __read_mostly = {
|
||||
.type = cpu_to_be16(ETH_P_DSA),
|
||||
.func = dsa_rcv,
|
||||
};
|
||||
|
||||
|
||||
+22
-12
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* net/dsa/tag_edsa.c - Ethertype DSA tagging
|
||||
* Copyright (c) 2008 Marvell Semiconductor
|
||||
* Copyright (c) 2008-2009 Marvell Semiconductor
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -45,7 +45,7 @@ int edsa_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
edsa_header[1] = ETH_P_EDSA & 0xff;
|
||||
edsa_header[2] = 0x00;
|
||||
edsa_header[3] = 0x00;
|
||||
edsa_header[4] = 0x60;
|
||||
edsa_header[4] = 0x60 | p->parent->index;
|
||||
edsa_header[5] = p->port << 3;
|
||||
|
||||
/*
|
||||
@@ -70,7 +70,7 @@ int edsa_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
edsa_header[1] = ETH_P_EDSA & 0xff;
|
||||
edsa_header[2] = 0x00;
|
||||
edsa_header[3] = 0x00;
|
||||
edsa_header[4] = 0x40;
|
||||
edsa_header[4] = 0x40 | p->parent->index;
|
||||
edsa_header[5] = p->port << 3;
|
||||
edsa_header[6] = 0x00;
|
||||
edsa_header[7] = 0x00;
|
||||
@@ -78,7 +78,7 @@ int edsa_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
|
||||
skb->protocol = htons(ETH_P_EDSA);
|
||||
|
||||
skb->dev = p->parent->master_netdev;
|
||||
skb->dev = p->parent->dst->master_netdev;
|
||||
dev_queue_xmit(skb);
|
||||
|
||||
return NETDEV_TX_OK;
|
||||
@@ -91,11 +91,13 @@ out_free:
|
||||
static int edsa_rcv(struct sk_buff *skb, struct net_device *dev,
|
||||
struct packet_type *pt, struct net_device *orig_dev)
|
||||
{
|
||||
struct dsa_switch *ds = dev->dsa_ptr;
|
||||
struct dsa_switch_tree *dst = dev->dsa_ptr;
|
||||
struct dsa_switch *ds;
|
||||
u8 *edsa_header;
|
||||
int source_device;
|
||||
int source_port;
|
||||
|
||||
if (unlikely(ds == NULL))
|
||||
if (unlikely(dst == NULL))
|
||||
goto out_drop;
|
||||
|
||||
skb = skb_unshare(skb, GFP_ATOMIC);
|
||||
@@ -111,16 +113,24 @@ static int edsa_rcv(struct sk_buff *skb, struct net_device *dev,
|
||||
edsa_header = skb->data + 2;
|
||||
|
||||
/*
|
||||
* Check that frame type is either TO_CPU or FORWARD, and
|
||||
* that the source device is zero.
|
||||
* Check that frame type is either TO_CPU or FORWARD.
|
||||
*/
|
||||
if ((edsa_header[0] & 0xdf) != 0x00 && (edsa_header[0] & 0xdf) != 0xc0)
|
||||
if ((edsa_header[0] & 0xc0) != 0x00 && (edsa_header[0] & 0xc0) != 0xc0)
|
||||
goto out_drop;
|
||||
|
||||
/*
|
||||
* Check that the source port is a registered DSA port.
|
||||
* Determine source device and port.
|
||||
*/
|
||||
source_device = edsa_header[0] & 0x1f;
|
||||
source_port = (edsa_header[1] >> 3) & 0x1f;
|
||||
|
||||
/*
|
||||
* Check that the source device exists and that the source
|
||||
* port is a registered DSA port.
|
||||
*/
|
||||
if (source_device >= dst->pd->nr_chips)
|
||||
goto out_drop;
|
||||
ds = dst->ds[source_device];
|
||||
if (source_port >= DSA_MAX_PORTS || ds->ports[source_port] == NULL)
|
||||
goto out_drop;
|
||||
|
||||
@@ -194,8 +204,8 @@ out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct packet_type edsa_packet_type = {
|
||||
.type = __constant_htons(ETH_P_EDSA),
|
||||
static struct packet_type edsa_packet_type __read_mostly = {
|
||||
.type = cpu_to_be16(ETH_P_EDSA),
|
||||
.func = edsa_rcv,
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* net/dsa/tag_trailer.c - Trailer tag format handling
|
||||
* Copyright (c) 2008 Marvell Semiconductor
|
||||
* Copyright (c) 2008-2009 Marvell Semiconductor
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -59,7 +59,7 @@ int trailer_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
|
||||
nskb->protocol = htons(ETH_P_TRAILER);
|
||||
|
||||
nskb->dev = p->parent->master_netdev;
|
||||
nskb->dev = p->parent->dst->master_netdev;
|
||||
dev_queue_xmit(nskb);
|
||||
|
||||
return NETDEV_TX_OK;
|
||||
@@ -68,12 +68,14 @@ int trailer_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
static int trailer_rcv(struct sk_buff *skb, struct net_device *dev,
|
||||
struct packet_type *pt, struct net_device *orig_dev)
|
||||
{
|
||||
struct dsa_switch *ds = dev->dsa_ptr;
|
||||
struct dsa_switch_tree *dst = dev->dsa_ptr;
|
||||
struct dsa_switch *ds;
|
||||
u8 *trailer;
|
||||
int source_port;
|
||||
|
||||
if (unlikely(ds == NULL))
|
||||
if (unlikely(dst == NULL))
|
||||
goto out_drop;
|
||||
ds = dst->ds[0];
|
||||
|
||||
skb = skb_unshare(skb, GFP_ATOMIC);
|
||||
if (skb == NULL)
|
||||
@@ -111,8 +113,8 @@ out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct packet_type trailer_packet_type = {
|
||||
.type = __constant_htons(ETH_P_TRAILER),
|
||||
static struct packet_type trailer_packet_type __read_mostly = {
|
||||
.type = cpu_to_be16(ETH_P_TRAILER),
|
||||
.func = trailer_rcv,
|
||||
};
|
||||
|
||||
|
||||
@@ -1102,8 +1102,8 @@ drop:
|
||||
return NET_RX_DROP;
|
||||
}
|
||||
|
||||
static struct packet_type econet_packet_type = {
|
||||
.type = __constant_htons(ETH_P_ECONET),
|
||||
static struct packet_type econet_packet_type __read_mostly = {
|
||||
.type = cpu_to_be16(ETH_P_ECONET),
|
||||
.func = econet_rcv,
|
||||
};
|
||||
|
||||
|
||||
+28
-22
@@ -35,7 +35,7 @@ config IP_ADVANCED_ROUTER
|
||||
|
||||
at boot time after the /proc file system has been mounted.
|
||||
|
||||
If you turn on IP forwarding, you will also get the rp_filter, which
|
||||
If you turn on IP forwarding, you should consider the rp_filter, which
|
||||
automatically rejects incoming packets if the routing table entry
|
||||
for their source address doesn't match the network interface they're
|
||||
arriving on. This has security advantages because it prevents the
|
||||
@@ -46,12 +46,16 @@ config IP_ADVANCED_ROUTER
|
||||
rp_filter on use:
|
||||
|
||||
echo 1 > /proc/sys/net/ipv4/conf/<device>/rp_filter
|
||||
or
|
||||
and
|
||||
echo 1 > /proc/sys/net/ipv4/conf/all/rp_filter
|
||||
|
||||
Note that some distributions enable it in startup scripts.
|
||||
For details about rp_filter strict and loose mode read
|
||||
<file:Documentation/networking/ip-sysctl.txt>.
|
||||
|
||||
If unsure, say N here.
|
||||
|
||||
choice
|
||||
choice
|
||||
prompt "Choose IP: FIB lookup algorithm (choose FIB_HASH if unsure)"
|
||||
depends on IP_ADVANCED_ROUTER
|
||||
default ASK_IP_FIB_HASH
|
||||
@@ -59,27 +63,29 @@ choice
|
||||
config ASK_IP_FIB_HASH
|
||||
bool "FIB_HASH"
|
||||
---help---
|
||||
Current FIB is very proven and good enough for most users.
|
||||
Current FIB is very proven and good enough for most users.
|
||||
|
||||
config IP_FIB_TRIE
|
||||
bool "FIB_TRIE"
|
||||
---help---
|
||||
Use new experimental LC-trie as FIB lookup algorithm.
|
||||
This improves lookup performance if you have a large
|
||||
number of routes.
|
||||
Use new experimental LC-trie as FIB lookup algorithm.
|
||||
This improves lookup performance if you have a large
|
||||
number of routes.
|
||||
|
||||
LC-trie is a longest matching prefix lookup algorithm which
|
||||
performs better than FIB_HASH for large routing tables.
|
||||
But, it consumes more memory and is more complex.
|
||||
|
||||
LC-trie is described in:
|
||||
|
||||
IP-address lookup using LC-tries. Stefan Nilsson and Gunnar Karlsson
|
||||
IEEE Journal on Selected Areas in Communications, 17(6):1083-1092,
|
||||
June 1999
|
||||
|
||||
An experimental study of compression methods for dynamic tries
|
||||
Stefan Nilsson and Matti Tikkanen. Algorithmica, 33(1):19-33, 2002.
|
||||
http://www.nada.kth.se/~snilsson/public/papers/dyntrie2/
|
||||
|
||||
LC-trie is a longest matching prefix lookup algorithm which
|
||||
performs better than FIB_HASH for large routing tables.
|
||||
But, it consumes more memory and is more complex.
|
||||
|
||||
LC-trie is described in:
|
||||
|
||||
IP-address lookup using LC-tries. Stefan Nilsson and Gunnar Karlsson
|
||||
IEEE Journal on Selected Areas in Communications, 17(6):1083-1092, June 1999
|
||||
An experimental study of compression methods for dynamic tries
|
||||
Stefan Nilsson and Matti Tikkanen. Algorithmica, 33(1):19-33, 2002.
|
||||
http://www.nada.kth.se/~snilsson/public/papers/dyntrie2/
|
||||
|
||||
endchoice
|
||||
|
||||
config IP_FIB_HASH
|
||||
@@ -191,7 +197,7 @@ config IP_PNP_RARP
|
||||
<file:Documentation/filesystems/nfsroot.txt> for details.
|
||||
|
||||
# not yet ready..
|
||||
# bool ' IP: ARP support' CONFIG_IP_PNP_ARP
|
||||
# bool ' IP: ARP support' CONFIG_IP_PNP_ARP
|
||||
config NET_IPIP
|
||||
tristate "IP: tunneling"
|
||||
select INET_TUNNEL
|
||||
@@ -361,7 +367,7 @@ config INET_IPCOMP
|
||||
---help---
|
||||
Support for IP Payload Compression Protocol (IPComp) (RFC3173),
|
||||
typically needed for IPsec.
|
||||
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
config INET_XFRM_TUNNEL
|
||||
@@ -415,7 +421,7 @@ config INET_DIAG
|
||||
Support for INET (TCP, DCCP, etc) socket monitoring interface used by
|
||||
native Linux tools such as ss. ss is included in iproute2, currently
|
||||
downloadable at <http://linux-net.osdl.org/index.php/Iproute2>.
|
||||
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
config INET_TCP_DIAG
|
||||
|
||||
+14
-14
@@ -369,7 +369,6 @@ lookup_protocol:
|
||||
sock_init_data(sock, sk);
|
||||
|
||||
sk->sk_destruct = inet_sock_destruct;
|
||||
sk->sk_family = PF_INET;
|
||||
sk->sk_protocol = protocol;
|
||||
sk->sk_backlog_rcv = sk->sk_prot->backlog_rcv;
|
||||
|
||||
@@ -1253,10 +1252,10 @@ static struct sk_buff **inet_gro_receive(struct sk_buff **head,
|
||||
int proto;
|
||||
int id;
|
||||
|
||||
if (unlikely(!pskb_may_pull(skb, sizeof(*iph))))
|
||||
iph = skb_gro_header(skb, sizeof(*iph));
|
||||
if (unlikely(!iph))
|
||||
goto out;
|
||||
|
||||
iph = ip_hdr(skb);
|
||||
proto = iph->protocol & (MAX_INET_PROTOS - 1);
|
||||
|
||||
rcu_read_lock();
|
||||
@@ -1264,13 +1263,13 @@ static struct sk_buff **inet_gro_receive(struct sk_buff **head,
|
||||
if (!ops || !ops->gro_receive)
|
||||
goto out_unlock;
|
||||
|
||||
if (iph->version != 4 || iph->ihl != 5)
|
||||
if (*(u8 *)iph != 0x45)
|
||||
goto out_unlock;
|
||||
|
||||
if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl)))
|
||||
goto out_unlock;
|
||||
|
||||
flush = ntohs(iph->tot_len) != skb->len ||
|
||||
flush = ntohs(iph->tot_len) != skb_gro_len(skb) ||
|
||||
iph->frag_off != htons(IP_DF);
|
||||
id = ntohs(iph->id);
|
||||
|
||||
@@ -1282,24 +1281,25 @@ static struct sk_buff **inet_gro_receive(struct sk_buff **head,
|
||||
|
||||
iph2 = ip_hdr(p);
|
||||
|
||||
if (iph->protocol != iph2->protocol ||
|
||||
iph->tos != iph2->tos ||
|
||||
memcmp(&iph->saddr, &iph2->saddr, 8)) {
|
||||
if ((iph->protocol ^ iph2->protocol) |
|
||||
(iph->tos ^ iph2->tos) |
|
||||
(iph->saddr ^ iph2->saddr) |
|
||||
(iph->daddr ^ iph2->daddr)) {
|
||||
NAPI_GRO_CB(p)->same_flow = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* All fields must match except length and checksum. */
|
||||
NAPI_GRO_CB(p)->flush |=
|
||||
memcmp(&iph->frag_off, &iph2->frag_off, 4) ||
|
||||
(u16)(ntohs(iph2->id) + NAPI_GRO_CB(p)->count) != id;
|
||||
(iph->ttl ^ iph2->ttl) |
|
||||
((u16)(ntohs(iph2->id) + NAPI_GRO_CB(p)->count) ^ id);
|
||||
|
||||
NAPI_GRO_CB(p)->flush |= flush;
|
||||
}
|
||||
|
||||
NAPI_GRO_CB(skb)->flush |= flush;
|
||||
__skb_pull(skb, sizeof(*iph));
|
||||
skb_reset_transport_header(skb);
|
||||
skb_gro_pull(skb, sizeof(*iph));
|
||||
skb_set_transport_header(skb, skb_gro_offset(skb));
|
||||
|
||||
pp = ops->gro_receive(head, skb);
|
||||
|
||||
@@ -1500,8 +1500,8 @@ static int ipv4_proc_init(void);
|
||||
* IP protocol layer initialiser
|
||||
*/
|
||||
|
||||
static struct packet_type ip_packet_type = {
|
||||
.type = __constant_htons(ETH_P_IP),
|
||||
static struct packet_type ip_packet_type __read_mostly = {
|
||||
.type = cpu_to_be16(ETH_P_IP),
|
||||
.func = ip_rcv,
|
||||
.gso_send_check = inet_gso_send_check,
|
||||
.gso_segment = inet_gso_segment,
|
||||
|
||||
+8
-5
@@ -801,8 +801,11 @@ static int arp_process(struct sk_buff *skb)
|
||||
* cache.
|
||||
*/
|
||||
|
||||
/* Special case: IPv4 duplicate address detection packet (RFC2131) */
|
||||
if (sip == 0) {
|
||||
/*
|
||||
* Special case: IPv4 duplicate address detection packet (RFC2131)
|
||||
* and Gratuitous ARP/ARP Announce. (RFC3927, Section 2.4)
|
||||
*/
|
||||
if (sip == 0 || tip == sip) {
|
||||
if (arp->ar_op == htons(ARPOP_REQUEST) &&
|
||||
inet_addr_type(net, tip) == RTN_LOCAL &&
|
||||
!arp_ignore(in_dev, sip, tip))
|
||||
@@ -892,7 +895,7 @@ static int arp_process(struct sk_buff *skb)
|
||||
out:
|
||||
if (in_dev)
|
||||
in_dev_put(in_dev);
|
||||
kfree_skb(skb);
|
||||
consume_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1225,8 +1228,8 @@ void arp_ifdown(struct net_device *dev)
|
||||
* Called once on startup.
|
||||
*/
|
||||
|
||||
static struct packet_type arp_packet_type = {
|
||||
.type = __constant_htons(ETH_P_ARP),
|
||||
static struct packet_type arp_packet_type __read_mostly = {
|
||||
.type = cpu_to_be16(ETH_P_ARP),
|
||||
.func = arp_rcv,
|
||||
};
|
||||
|
||||
|
||||
+118
-12
@@ -1942,23 +1942,85 @@ socket_setattr_failure:
|
||||
}
|
||||
|
||||
/**
|
||||
* cipso_v4_sock_delattr - Delete the CIPSO option from a socket
|
||||
* @sk: the socket
|
||||
* cipso_v4_req_setattr - Add a CIPSO option to a connection request socket
|
||||
* @req: the connection request socket
|
||||
* @doi_def: the CIPSO DOI to use
|
||||
* @secattr: the specific security attributes of the socket
|
||||
*
|
||||
* Description:
|
||||
* Removes the CIPSO option from a socket, if present.
|
||||
* Set the CIPSO option on the given socket using the DOI definition and
|
||||
* security attributes passed to the function. Returns zero on success and
|
||||
* negative values on failure.
|
||||
*
|
||||
*/
|
||||
void cipso_v4_sock_delattr(struct sock *sk)
|
||||
int cipso_v4_req_setattr(struct request_sock *req,
|
||||
const struct cipso_v4_doi *doi_def,
|
||||
const struct netlbl_lsm_secattr *secattr)
|
||||
{
|
||||
u8 hdr_delta;
|
||||
struct ip_options *opt;
|
||||
struct inet_sock *sk_inet;
|
||||
int ret_val = -EPERM;
|
||||
unsigned char *buf = NULL;
|
||||
u32 buf_len;
|
||||
u32 opt_len;
|
||||
struct ip_options *opt = NULL;
|
||||
struct inet_request_sock *req_inet;
|
||||
|
||||
sk_inet = inet_sk(sk);
|
||||
opt = sk_inet->opt;
|
||||
if (opt == NULL || opt->cipso == 0)
|
||||
return;
|
||||
/* We allocate the maximum CIPSO option size here so we are probably
|
||||
* being a little wasteful, but it makes our life _much_ easier later
|
||||
* on and after all we are only talking about 40 bytes. */
|
||||
buf_len = CIPSO_V4_OPT_LEN_MAX;
|
||||
buf = kmalloc(buf_len, GFP_ATOMIC);
|
||||
if (buf == NULL) {
|
||||
ret_val = -ENOMEM;
|
||||
goto req_setattr_failure;
|
||||
}
|
||||
|
||||
ret_val = cipso_v4_genopt(buf, buf_len, doi_def, secattr);
|
||||
if (ret_val < 0)
|
||||
goto req_setattr_failure;
|
||||
buf_len = ret_val;
|
||||
|
||||
/* We can't use ip_options_get() directly because it makes a call to
|
||||
* ip_options_get_alloc() which allocates memory with GFP_KERNEL and
|
||||
* we won't always have CAP_NET_RAW even though we _always_ want to
|
||||
* set the IPOPT_CIPSO option. */
|
||||
opt_len = (buf_len + 3) & ~3;
|
||||
opt = kzalloc(sizeof(*opt) + opt_len, GFP_ATOMIC);
|
||||
if (opt == NULL) {
|
||||
ret_val = -ENOMEM;
|
||||
goto req_setattr_failure;
|
||||
}
|
||||
memcpy(opt->__data, buf, buf_len);
|
||||
opt->optlen = opt_len;
|
||||
opt->cipso = sizeof(struct iphdr);
|
||||
kfree(buf);
|
||||
buf = NULL;
|
||||
|
||||
req_inet = inet_rsk(req);
|
||||
opt = xchg(&req_inet->opt, opt);
|
||||
kfree(opt);
|
||||
|
||||
return 0;
|
||||
|
||||
req_setattr_failure:
|
||||
kfree(buf);
|
||||
kfree(opt);
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
/**
|
||||
* cipso_v4_delopt - Delete the CIPSO option from a set of IP options
|
||||
* @opt_ptr: IP option pointer
|
||||
*
|
||||
* Description:
|
||||
* Deletes the CIPSO IP option from a set of IP options and makes the necessary
|
||||
* adjustments to the IP option structure. Returns zero on success, negative
|
||||
* values on failure.
|
||||
*
|
||||
*/
|
||||
int cipso_v4_delopt(struct ip_options **opt_ptr)
|
||||
{
|
||||
int hdr_delta = 0;
|
||||
struct ip_options *opt = *opt_ptr;
|
||||
|
||||
if (opt->srr || opt->rr || opt->ts || opt->router_alert) {
|
||||
u8 cipso_len;
|
||||
@@ -2003,11 +2065,34 @@ void cipso_v4_sock_delattr(struct sock *sk)
|
||||
} else {
|
||||
/* only the cipso option was present on the socket so we can
|
||||
* remove the entire option struct */
|
||||
sk_inet->opt = NULL;
|
||||
*opt_ptr = NULL;
|
||||
hdr_delta = opt->optlen;
|
||||
kfree(opt);
|
||||
}
|
||||
|
||||
return hdr_delta;
|
||||
}
|
||||
|
||||
/**
|
||||
* cipso_v4_sock_delattr - Delete the CIPSO option from a socket
|
||||
* @sk: the socket
|
||||
*
|
||||
* Description:
|
||||
* Removes the CIPSO option from a socket, if present.
|
||||
*
|
||||
*/
|
||||
void cipso_v4_sock_delattr(struct sock *sk)
|
||||
{
|
||||
int hdr_delta;
|
||||
struct ip_options *opt;
|
||||
struct inet_sock *sk_inet;
|
||||
|
||||
sk_inet = inet_sk(sk);
|
||||
opt = sk_inet->opt;
|
||||
if (opt == NULL || opt->cipso == 0)
|
||||
return;
|
||||
|
||||
hdr_delta = cipso_v4_delopt(&sk_inet->opt);
|
||||
if (sk_inet->is_icsk && hdr_delta > 0) {
|
||||
struct inet_connection_sock *sk_conn = inet_csk(sk);
|
||||
sk_conn->icsk_ext_hdr_len -= hdr_delta;
|
||||
@@ -2015,6 +2100,27 @@ void cipso_v4_sock_delattr(struct sock *sk)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* cipso_v4_req_delattr - Delete the CIPSO option from a request socket
|
||||
* @reg: the request socket
|
||||
*
|
||||
* Description:
|
||||
* Removes the CIPSO option from a request socket, if present.
|
||||
*
|
||||
*/
|
||||
void cipso_v4_req_delattr(struct request_sock *req)
|
||||
{
|
||||
struct ip_options *opt;
|
||||
struct inet_request_sock *req_inet;
|
||||
|
||||
req_inet = inet_rsk(req);
|
||||
opt = req_inet->opt;
|
||||
if (opt == NULL || opt->cipso == 0)
|
||||
return;
|
||||
|
||||
cipso_v4_delopt(&req_inet->opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* cipso_v4_getattr - Helper function for the cipso_v4_*_getattr functions
|
||||
* @cipso: the CIPSO v4 option
|
||||
|
||||
+11
-1
@@ -1075,6 +1075,14 @@ static int inetdev_event(struct notifier_block *this, unsigned long event,
|
||||
}
|
||||
}
|
||||
ip_mc_up(in_dev);
|
||||
/* fall through */
|
||||
case NETDEV_CHANGEADDR:
|
||||
if (IN_DEV_ARP_NOTIFY(in_dev))
|
||||
arp_send(ARPOP_REQUEST, ETH_P_ARP,
|
||||
in_dev->ifa_list->ifa_address,
|
||||
dev,
|
||||
in_dev->ifa_list->ifa_address,
|
||||
NULL, dev->dev_addr, NULL);
|
||||
break;
|
||||
case NETDEV_DOWN:
|
||||
ip_mc_down(in_dev);
|
||||
@@ -1208,7 +1216,8 @@ static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh,
|
||||
kfree_skb(skb);
|
||||
goto errout;
|
||||
}
|
||||
err = rtnl_notify(skb, net, pid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL);
|
||||
rtnl_notify(skb, net, pid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL);
|
||||
return;
|
||||
errout:
|
||||
if (err < 0)
|
||||
rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err);
|
||||
@@ -1439,6 +1448,7 @@ static struct devinet_sysctl_table {
|
||||
DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"),
|
||||
DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"),
|
||||
DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"),
|
||||
DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"),
|
||||
|
||||
DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"),
|
||||
DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"),
|
||||
|
||||
@@ -275,7 +275,7 @@ int fib_validate_source(__be32 src, __be32 dst, u8 tos, int oif,
|
||||
fib_res_put(&res);
|
||||
if (no_addr)
|
||||
goto last_resort;
|
||||
if (rpf)
|
||||
if (rpf == 1)
|
||||
goto e_inval;
|
||||
fl.oif = dev->ifindex;
|
||||
|
||||
|
||||
@@ -322,8 +322,9 @@ void rtmsg_fib(int event, __be32 key, struct fib_alias *fa,
|
||||
kfree_skb(skb);
|
||||
goto errout;
|
||||
}
|
||||
err = rtnl_notify(skb, info->nl_net, info->pid, RTNLGRP_IPV4_ROUTE,
|
||||
info->nlh, GFP_KERNEL);
|
||||
rtnl_notify(skb, info->nl_net, info->pid, RTNLGRP_IPV4_ROUTE,
|
||||
info->nlh, GFP_KERNEL);
|
||||
return;
|
||||
errout:
|
||||
if (err < 0)
|
||||
rtnl_set_sk_err(info->nl_net, RTNLGRP_IPV4_ROUTE, err);
|
||||
|
||||
@@ -375,6 +375,7 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb)
|
||||
inet->tos = ip_hdr(skb)->tos;
|
||||
daddr = ipc.addr = rt->rt_src;
|
||||
ipc.opt = NULL;
|
||||
ipc.shtx.flags = 0;
|
||||
if (icmp_param->replyopts.optlen) {
|
||||
ipc.opt = &icmp_param->replyopts;
|
||||
if (ipc.opt->srr)
|
||||
@@ -532,6 +533,7 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info)
|
||||
inet_sk(sk)->tos = tos;
|
||||
ipc.addr = iph->saddr;
|
||||
ipc.opt = &icmp_param.replyopts;
|
||||
ipc.shtx.flags = 0;
|
||||
|
||||
{
|
||||
struct flowi fl = {
|
||||
|
||||
@@ -93,24 +93,40 @@ int inet_csk_get_port(struct sock *sk, unsigned short snum)
|
||||
struct inet_bind_hashbucket *head;
|
||||
struct hlist_node *node;
|
||||
struct inet_bind_bucket *tb;
|
||||
int ret;
|
||||
int ret, attempts = 5;
|
||||
struct net *net = sock_net(sk);
|
||||
int smallest_size = -1, smallest_rover;
|
||||
|
||||
local_bh_disable();
|
||||
if (!snum) {
|
||||
int remaining, rover, low, high;
|
||||
|
||||
again:
|
||||
inet_get_local_port_range(&low, &high);
|
||||
remaining = (high - low) + 1;
|
||||
rover = net_random() % remaining + low;
|
||||
smallest_rover = rover = net_random() % remaining + low;
|
||||
|
||||
smallest_size = -1;
|
||||
do {
|
||||
head = &hashinfo->bhash[inet_bhashfn(net, rover,
|
||||
hashinfo->bhash_size)];
|
||||
spin_lock(&head->lock);
|
||||
inet_bind_bucket_for_each(tb, node, &head->chain)
|
||||
if (ib_net(tb) == net && tb->port == rover)
|
||||
if (ib_net(tb) == net && tb->port == rover) {
|
||||
if (tb->fastreuse > 0 &&
|
||||
sk->sk_reuse &&
|
||||
sk->sk_state != TCP_LISTEN &&
|
||||
(tb->num_owners < smallest_size || smallest_size == -1)) {
|
||||
smallest_size = tb->num_owners;
|
||||
smallest_rover = rover;
|
||||
if (atomic_read(&hashinfo->bsockets) > (high - low) + 1) {
|
||||
spin_unlock(&head->lock);
|
||||
snum = smallest_rover;
|
||||
goto have_snum;
|
||||
}
|
||||
}
|
||||
goto next;
|
||||
}
|
||||
break;
|
||||
next:
|
||||
spin_unlock(&head->lock);
|
||||
@@ -125,14 +141,19 @@ int inet_csk_get_port(struct sock *sk, unsigned short snum)
|
||||
* the top level, not from the 'break;' statement.
|
||||
*/
|
||||
ret = 1;
|
||||
if (remaining <= 0)
|
||||
if (remaining <= 0) {
|
||||
if (smallest_size != -1) {
|
||||
snum = smallest_rover;
|
||||
goto have_snum;
|
||||
}
|
||||
goto fail;
|
||||
|
||||
}
|
||||
/* OK, here is the one we will use. HEAD is
|
||||
* non-NULL and we hold it's mutex.
|
||||
*/
|
||||
snum = rover;
|
||||
} else {
|
||||
have_snum:
|
||||
head = &hashinfo->bhash[inet_bhashfn(net, snum,
|
||||
hashinfo->bhash_size)];
|
||||
spin_lock(&head->lock);
|
||||
@@ -145,12 +166,19 @@ int inet_csk_get_port(struct sock *sk, unsigned short snum)
|
||||
tb_found:
|
||||
if (!hlist_empty(&tb->owners)) {
|
||||
if (tb->fastreuse > 0 &&
|
||||
sk->sk_reuse && sk->sk_state != TCP_LISTEN) {
|
||||
sk->sk_reuse && sk->sk_state != TCP_LISTEN &&
|
||||
smallest_size == -1) {
|
||||
goto success;
|
||||
} else {
|
||||
ret = 1;
|
||||
if (inet_csk(sk)->icsk_af_ops->bind_conflict(sk, tb))
|
||||
if (inet_csk(sk)->icsk_af_ops->bind_conflict(sk, tb)) {
|
||||
if (sk->sk_reuse && sk->sk_state != TCP_LISTEN &&
|
||||
smallest_size != -1 && --attempts >= 0) {
|
||||
spin_unlock(&head->lock);
|
||||
goto again;
|
||||
}
|
||||
goto fail_unlock;
|
||||
}
|
||||
}
|
||||
}
|
||||
tb_not_found:
|
||||
|
||||
@@ -267,6 +267,7 @@ static struct inet_frag_queue *inet_frag_create(struct netns_frags *nf,
|
||||
|
||||
struct inet_frag_queue *inet_frag_find(struct netns_frags *nf,
|
||||
struct inet_frags *f, void *key, unsigned int hash)
|
||||
__releases(&f->lock)
|
||||
{
|
||||
struct inet_frag_queue *q;
|
||||
struct hlist_node *n;
|
||||
|
||||
@@ -38,6 +38,7 @@ struct inet_bind_bucket *inet_bind_bucket_create(struct kmem_cache *cachep,
|
||||
write_pnet(&tb->ib_net, hold_net(net));
|
||||
tb->port = snum;
|
||||
tb->fastreuse = 0;
|
||||
tb->num_owners = 0;
|
||||
INIT_HLIST_HEAD(&tb->owners);
|
||||
hlist_add_head(&tb->node, &head->chain);
|
||||
}
|
||||
@@ -59,8 +60,13 @@ void inet_bind_bucket_destroy(struct kmem_cache *cachep, struct inet_bind_bucket
|
||||
void inet_bind_hash(struct sock *sk, struct inet_bind_bucket *tb,
|
||||
const unsigned short snum)
|
||||
{
|
||||
struct inet_hashinfo *hashinfo = sk->sk_prot->h.hashinfo;
|
||||
|
||||
atomic_inc(&hashinfo->bsockets);
|
||||
|
||||
inet_sk(sk)->num = snum;
|
||||
sk_add_bind_node(sk, &tb->owners);
|
||||
tb->num_owners++;
|
||||
inet_csk(sk)->icsk_bind_hash = tb;
|
||||
}
|
||||
|
||||
@@ -75,9 +81,12 @@ static void __inet_put_port(struct sock *sk)
|
||||
struct inet_bind_hashbucket *head = &hashinfo->bhash[bhash];
|
||||
struct inet_bind_bucket *tb;
|
||||
|
||||
atomic_dec(&hashinfo->bsockets);
|
||||
|
||||
spin_lock(&head->lock);
|
||||
tb = inet_csk(sk)->icsk_bind_hash;
|
||||
__sk_del_bind_node(sk);
|
||||
tb->num_owners--;
|
||||
inet_csk(sk)->icsk_bind_hash = NULL;
|
||||
inet_sk(sk)->num = 0;
|
||||
inet_bind_bucket_destroy(hashinfo->bind_bucket_cachep, tb);
|
||||
@@ -444,9 +453,9 @@ int __inet_hash_connect(struct inet_timewait_death_row *death_row,
|
||||
*/
|
||||
inet_bind_bucket_for_each(tb, node, &head->chain) {
|
||||
if (ib_net(tb) == net && tb->port == port) {
|
||||
WARN_ON(hlist_empty(&tb->owners));
|
||||
if (tb->fastreuse >= 0)
|
||||
goto next_port;
|
||||
WARN_ON(hlist_empty(&tb->owners));
|
||||
if (!check_established(death_row, sk,
|
||||
port, &tw))
|
||||
goto ok;
|
||||
@@ -523,6 +532,7 @@ void inet_hashinfo_init(struct inet_hashinfo *h)
|
||||
{
|
||||
int i;
|
||||
|
||||
atomic_set(&h->bsockets, 0);
|
||||
for (i = 0; i < INET_LHTABLE_SIZE; i++) {
|
||||
spin_lock_init(&h->listening_hash[i].lock);
|
||||
INIT_HLIST_NULLS_HEAD(&h->listening_hash[i].head,
|
||||
|
||||
@@ -463,6 +463,7 @@ err:
|
||||
static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev,
|
||||
struct net_device *dev)
|
||||
{
|
||||
struct net *net = container_of(qp->q.net, struct net, ipv4.frags);
|
||||
struct iphdr *iph;
|
||||
struct sk_buff *fp, *head = qp->q.fragments;
|
||||
int len;
|
||||
@@ -548,7 +549,7 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev,
|
||||
iph = ip_hdr(head);
|
||||
iph->frag_off = 0;
|
||||
iph->tot_len = htons(len);
|
||||
IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_REASMOKS);
|
||||
IP_INC_STATS_BH(net, IPSTATS_MIB_REASMOKS);
|
||||
qp->q.fragments = NULL;
|
||||
return 0;
|
||||
|
||||
|
||||
+98
-38
@@ -164,67 +164,124 @@ static DEFINE_RWLOCK(ipgre_lock);
|
||||
|
||||
/* Given src, dst and key, find appropriate for input tunnel. */
|
||||
|
||||
static struct ip_tunnel * ipgre_tunnel_lookup(struct net *net,
|
||||
static struct ip_tunnel * ipgre_tunnel_lookup(struct net_device *dev,
|
||||
__be32 remote, __be32 local,
|
||||
__be32 key, __be16 gre_proto)
|
||||
{
|
||||
struct net *net = dev_net(dev);
|
||||
int link = dev->ifindex;
|
||||
unsigned h0 = HASH(remote);
|
||||
unsigned h1 = HASH(key);
|
||||
struct ip_tunnel *t;
|
||||
struct ip_tunnel *t2 = NULL;
|
||||
struct ip_tunnel *t, *cand = NULL;
|
||||
struct ipgre_net *ign = net_generic(net, ipgre_net_id);
|
||||
int dev_type = (gre_proto == htons(ETH_P_TEB)) ?
|
||||
ARPHRD_ETHER : ARPHRD_IPGRE;
|
||||
int score, cand_score = 4;
|
||||
|
||||
for (t = ign->tunnels_r_l[h0^h1]; t; t = t->next) {
|
||||
if (local == t->parms.iph.saddr && remote == t->parms.iph.daddr) {
|
||||
if (t->parms.i_key == key && t->dev->flags & IFF_UP) {
|
||||
if (t->dev->type == dev_type)
|
||||
return t;
|
||||
if (t->dev->type == ARPHRD_IPGRE && !t2)
|
||||
t2 = t;
|
||||
}
|
||||
if (local != t->parms.iph.saddr ||
|
||||
remote != t->parms.iph.daddr ||
|
||||
key != t->parms.i_key ||
|
||||
!(t->dev->flags & IFF_UP))
|
||||
continue;
|
||||
|
||||
if (t->dev->type != ARPHRD_IPGRE &&
|
||||
t->dev->type != dev_type)
|
||||
continue;
|
||||
|
||||
score = 0;
|
||||
if (t->parms.link != link)
|
||||
score |= 1;
|
||||
if (t->dev->type != dev_type)
|
||||
score |= 2;
|
||||
if (score == 0)
|
||||
return t;
|
||||
|
||||
if (score < cand_score) {
|
||||
cand = t;
|
||||
cand_score = score;
|
||||
}
|
||||
}
|
||||
|
||||
for (t = ign->tunnels_r[h0^h1]; t; t = t->next) {
|
||||
if (remote == t->parms.iph.daddr) {
|
||||
if (t->parms.i_key == key && t->dev->flags & IFF_UP) {
|
||||
if (t->dev->type == dev_type)
|
||||
return t;
|
||||
if (t->dev->type == ARPHRD_IPGRE && !t2)
|
||||
t2 = t;
|
||||
}
|
||||
if (remote != t->parms.iph.daddr ||
|
||||
key != t->parms.i_key ||
|
||||
!(t->dev->flags & IFF_UP))
|
||||
continue;
|
||||
|
||||
if (t->dev->type != ARPHRD_IPGRE &&
|
||||
t->dev->type != dev_type)
|
||||
continue;
|
||||
|
||||
score = 0;
|
||||
if (t->parms.link != link)
|
||||
score |= 1;
|
||||
if (t->dev->type != dev_type)
|
||||
score |= 2;
|
||||
if (score == 0)
|
||||
return t;
|
||||
|
||||
if (score < cand_score) {
|
||||
cand = t;
|
||||
cand_score = score;
|
||||
}
|
||||
}
|
||||
|
||||
for (t = ign->tunnels_l[h1]; t; t = t->next) {
|
||||
if (local == t->parms.iph.saddr ||
|
||||
(local == t->parms.iph.daddr &&
|
||||
ipv4_is_multicast(local))) {
|
||||
if (t->parms.i_key == key && t->dev->flags & IFF_UP) {
|
||||
if (t->dev->type == dev_type)
|
||||
return t;
|
||||
if (t->dev->type == ARPHRD_IPGRE && !t2)
|
||||
t2 = t;
|
||||
}
|
||||
if ((local != t->parms.iph.saddr &&
|
||||
(local != t->parms.iph.daddr ||
|
||||
!ipv4_is_multicast(local))) ||
|
||||
key != t->parms.i_key ||
|
||||
!(t->dev->flags & IFF_UP))
|
||||
continue;
|
||||
|
||||
if (t->dev->type != ARPHRD_IPGRE &&
|
||||
t->dev->type != dev_type)
|
||||
continue;
|
||||
|
||||
score = 0;
|
||||
if (t->parms.link != link)
|
||||
score |= 1;
|
||||
if (t->dev->type != dev_type)
|
||||
score |= 2;
|
||||
if (score == 0)
|
||||
return t;
|
||||
|
||||
if (score < cand_score) {
|
||||
cand = t;
|
||||
cand_score = score;
|
||||
}
|
||||
}
|
||||
|
||||
for (t = ign->tunnels_wc[h1]; t; t = t->next) {
|
||||
if (t->parms.i_key == key && t->dev->flags & IFF_UP) {
|
||||
if (t->dev->type == dev_type)
|
||||
return t;
|
||||
if (t->dev->type == ARPHRD_IPGRE && !t2)
|
||||
t2 = t;
|
||||
if (t->parms.i_key != key ||
|
||||
!(t->dev->flags & IFF_UP))
|
||||
continue;
|
||||
|
||||
if (t->dev->type != ARPHRD_IPGRE &&
|
||||
t->dev->type != dev_type)
|
||||
continue;
|
||||
|
||||
score = 0;
|
||||
if (t->parms.link != link)
|
||||
score |= 1;
|
||||
if (t->dev->type != dev_type)
|
||||
score |= 2;
|
||||
if (score == 0)
|
||||
return t;
|
||||
|
||||
if (score < cand_score) {
|
||||
cand = t;
|
||||
cand_score = score;
|
||||
}
|
||||
}
|
||||
|
||||
if (t2)
|
||||
return t2;
|
||||
if (cand != NULL)
|
||||
return cand;
|
||||
|
||||
if (ign->fb_tunnel_dev->flags&IFF_UP)
|
||||
if (ign->fb_tunnel_dev->flags & IFF_UP)
|
||||
return netdev_priv(ign->fb_tunnel_dev);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -284,6 +341,7 @@ static struct ip_tunnel *ipgre_tunnel_find(struct net *net,
|
||||
__be32 remote = parms->iph.daddr;
|
||||
__be32 local = parms->iph.saddr;
|
||||
__be32 key = parms->i_key;
|
||||
int link = parms->link;
|
||||
struct ip_tunnel *t, **tp;
|
||||
struct ipgre_net *ign = net_generic(net, ipgre_net_id);
|
||||
|
||||
@@ -291,6 +349,7 @@ static struct ip_tunnel *ipgre_tunnel_find(struct net *net,
|
||||
if (local == t->parms.iph.saddr &&
|
||||
remote == t->parms.iph.daddr &&
|
||||
key == t->parms.i_key &&
|
||||
link == t->parms.link &&
|
||||
type == t->dev->type)
|
||||
break;
|
||||
|
||||
@@ -421,7 +480,7 @@ static void ipgre_err(struct sk_buff *skb, u32 info)
|
||||
}
|
||||
|
||||
read_lock(&ipgre_lock);
|
||||
t = ipgre_tunnel_lookup(dev_net(skb->dev), iph->daddr, iph->saddr,
|
||||
t = ipgre_tunnel_lookup(skb->dev, iph->daddr, iph->saddr,
|
||||
flags & GRE_KEY ?
|
||||
*(((__be32 *)p) + (grehlen / 4) - 1) : 0,
|
||||
p[1]);
|
||||
@@ -432,7 +491,7 @@ static void ipgre_err(struct sk_buff *skb, u32 info)
|
||||
if (t->parms.iph.ttl == 0 && type == ICMP_TIME_EXCEEDED)
|
||||
goto out;
|
||||
|
||||
if (jiffies - t->err_time < IPTUNNEL_ERR_TIMEO)
|
||||
if (time_before(jiffies, t->err_time + IPTUNNEL_ERR_TIMEO))
|
||||
t->err_count++;
|
||||
else
|
||||
t->err_count = 1;
|
||||
@@ -518,7 +577,7 @@ static int ipgre_rcv(struct sk_buff *skb)
|
||||
gre_proto = *(__be16 *)(h + 2);
|
||||
|
||||
read_lock(&ipgre_lock);
|
||||
if ((tunnel = ipgre_tunnel_lookup(dev_net(skb->dev),
|
||||
if ((tunnel = ipgre_tunnel_lookup(skb->dev,
|
||||
iph->saddr, iph->daddr, key,
|
||||
gre_proto))) {
|
||||
struct net_device_stats *stats = &tunnel->dev->stats;
|
||||
@@ -744,7 +803,8 @@ static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
#endif
|
||||
|
||||
if (tunnel->err_count > 0) {
|
||||
if (jiffies - tunnel->err_time < IPTUNNEL_ERR_TIMEO) {
|
||||
if (time_before(jiffies,
|
||||
tunnel->err_time + IPTUNNEL_ERR_TIMEO)) {
|
||||
tunnel->err_count--;
|
||||
|
||||
dst_link_failure(skb);
|
||||
|
||||
@@ -935,6 +935,10 @@ alloc_new_skb:
|
||||
sk->sk_allocation);
|
||||
if (unlikely(skb == NULL))
|
||||
err = -ENOBUFS;
|
||||
else
|
||||
/* only the initial fragment is
|
||||
time stamped */
|
||||
ipc->shtx.flags = 0;
|
||||
}
|
||||
if (skb == NULL)
|
||||
goto error;
|
||||
@@ -945,6 +949,7 @@ alloc_new_skb:
|
||||
skb->ip_summed = csummode;
|
||||
skb->csum = 0;
|
||||
skb_reserve(skb, hh_len);
|
||||
*skb_tx(skb) = ipc->shtx;
|
||||
|
||||
/*
|
||||
* Find where to start putting bytes.
|
||||
@@ -1364,6 +1369,7 @@ void ip_send_reply(struct sock *sk, struct sk_buff *skb, struct ip_reply_arg *ar
|
||||
|
||||
daddr = ipc.addr = rt->rt_src;
|
||||
ipc.opt = NULL;
|
||||
ipc.shtx.flags = 0;
|
||||
|
||||
if (replyopts.opt.optlen) {
|
||||
ipc.opt = &replyopts.opt;
|
||||
|
||||
+4
-4
@@ -100,8 +100,8 @@
|
||||
#define CONF_NAMESERVERS_MAX 3 /* Maximum number of nameservers
|
||||
- '3' from resolv.h */
|
||||
|
||||
#define NONE __constant_htonl(INADDR_NONE)
|
||||
#define ANY __constant_htonl(INADDR_ANY)
|
||||
#define NONE cpu_to_be32(INADDR_NONE)
|
||||
#define ANY cpu_to_be32(INADDR_ANY)
|
||||
|
||||
/*
|
||||
* Public IP configuration
|
||||
@@ -406,7 +406,7 @@ static int __init ic_defaults(void)
|
||||
static int ic_rarp_recv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev);
|
||||
|
||||
static struct packet_type rarp_packet_type __initdata = {
|
||||
.type = __constant_htons(ETH_P_RARP),
|
||||
.type = cpu_to_be16(ETH_P_RARP),
|
||||
.func = ic_rarp_recv,
|
||||
};
|
||||
|
||||
@@ -568,7 +568,7 @@ struct bootp_pkt { /* BOOTP packet format */
|
||||
static int ic_bootp_recv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev);
|
||||
|
||||
static struct packet_type bootp_packet_type __initdata = {
|
||||
.type = __constant_htons(ETH_P_IP),
|
||||
.type = cpu_to_be16(ETH_P_IP),
|
||||
.func = ic_bootp_recv,
|
||||
};
|
||||
|
||||
|
||||
+4
-3
@@ -327,7 +327,7 @@ static int ipip_err(struct sk_buff *skb, u32 info)
|
||||
if (t->parms.iph.ttl == 0 && type == ICMP_TIME_EXCEEDED)
|
||||
goto out;
|
||||
|
||||
if (jiffies - t->err_time < IPTUNNEL_ERR_TIMEO)
|
||||
if (time_before(jiffies, t->err_time + IPTUNNEL_ERR_TIMEO))
|
||||
t->err_count++;
|
||||
else
|
||||
t->err_count = 1;
|
||||
@@ -466,7 +466,8 @@ static int ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
}
|
||||
|
||||
if (tunnel->err_count > 0) {
|
||||
if (jiffies - tunnel->err_time < IPTUNNEL_ERR_TIMEO) {
|
||||
if (time_before(jiffies,
|
||||
tunnel->err_time + IPTUNNEL_ERR_TIMEO)) {
|
||||
tunnel->err_count--;
|
||||
dst_link_failure(skb);
|
||||
} else
|
||||
@@ -750,7 +751,7 @@ static struct xfrm_tunnel ipip_handler = {
|
||||
.priority = 1,
|
||||
};
|
||||
|
||||
static char banner[] __initdata =
|
||||
static const char banner[] __initconst =
|
||||
KERN_INFO "IPv4 over IPv4 tunneling driver\n";
|
||||
|
||||
static void ipip_destroy_tunnels(struct ipip_net *ipn)
|
||||
|
||||
+282
-182
File diff suppressed because it is too large
Load Diff
+12
-18
@@ -31,7 +31,7 @@ config NF_CONNTRACK_PROC_COMPAT
|
||||
default y
|
||||
help
|
||||
This option enables /proc and sysctl compatibility with the old
|
||||
layer 3 dependant connection tracking. This is needed to keep
|
||||
layer 3 dependent connection tracking. This is needed to keep
|
||||
old programs that have not been adapted to the new names working.
|
||||
|
||||
If unsure, say Y.
|
||||
@@ -95,11 +95,11 @@ config IP_NF_MATCH_ECN
|
||||
config IP_NF_MATCH_TTL
|
||||
tristate '"ttl" match support'
|
||||
depends on NETFILTER_ADVANCED
|
||||
help
|
||||
This adds CONFIG_IP_NF_MATCH_TTL option, which enabled the user
|
||||
to match packets by their TTL value.
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
select NETFILTER_XT_MATCH_HL
|
||||
---help---
|
||||
This is a backwards-compat option for the user's convenience
|
||||
(e.g. when running oldconfig). It selects
|
||||
CONFIG_NETFILTER_XT_MATCH_HL.
|
||||
|
||||
# `filter', generic and specific targets
|
||||
config IP_NF_FILTER
|
||||
@@ -323,19 +323,13 @@ config IP_NF_TARGET_ECN
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
config IP_NF_TARGET_TTL
|
||||
tristate 'TTL target support'
|
||||
depends on IP_NF_MANGLE
|
||||
tristate '"TTL" target support'
|
||||
depends on NETFILTER_ADVANCED
|
||||
help
|
||||
This option adds a `TTL' target, which enables the user to modify
|
||||
the TTL value of the IP header.
|
||||
|
||||
While it is safe to decrement/lower the TTL, this target also enables
|
||||
functionality to increment and set the TTL value of the IP header to
|
||||
arbitrary values. This is EXTREMELY DANGEROUS since you can easily
|
||||
create immortal packets that loop forever on the network.
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
select NETFILTER_XT_TARGET_HL
|
||||
---help---
|
||||
This is a backwards-compat option for the user's convenience
|
||||
(e.g. when running oldconfig). It selects
|
||||
CONFIG_NETFILTER_XT_TARGET_HL.
|
||||
|
||||
# raw + specific targets
|
||||
config IP_NF_RAW
|
||||
|
||||
@@ -51,7 +51,6 @@ obj-$(CONFIG_IP_NF_SECURITY) += iptable_security.o
|
||||
obj-$(CONFIG_IP_NF_MATCH_ADDRTYPE) += ipt_addrtype.o
|
||||
obj-$(CONFIG_IP_NF_MATCH_AH) += ipt_ah.o
|
||||
obj-$(CONFIG_IP_NF_MATCH_ECN) += ipt_ecn.o
|
||||
obj-$(CONFIG_IP_NF_MATCH_TTL) += ipt_ttl.o
|
||||
|
||||
# targets
|
||||
obj-$(CONFIG_IP_NF_TARGET_CLUSTERIP) += ipt_CLUSTERIP.o
|
||||
@@ -61,7 +60,6 @@ obj-$(CONFIG_IP_NF_TARGET_MASQUERADE) += ipt_MASQUERADE.o
|
||||
obj-$(CONFIG_IP_NF_TARGET_NETMAP) += ipt_NETMAP.o
|
||||
obj-$(CONFIG_IP_NF_TARGET_REDIRECT) += ipt_REDIRECT.o
|
||||
obj-$(CONFIG_IP_NF_TARGET_REJECT) += ipt_REJECT.o
|
||||
obj-$(CONFIG_IP_NF_TARGET_TTL) += ipt_TTL.o
|
||||
obj-$(CONFIG_IP_NF_TARGET_ULOG) += ipt_ULOG.o
|
||||
|
||||
# generic ARP tables
|
||||
|
||||
+117
-38
@@ -73,6 +73,28 @@ static inline int arp_devaddr_compare(const struct arpt_devaddr_info *ap,
|
||||
return (ret != 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Unfortunatly, _b and _mask are not aligned to an int (or long int)
|
||||
* Some arches dont care, unrolling the loop is a win on them.
|
||||
* For other arches, we only have a 16bit alignement.
|
||||
*/
|
||||
static unsigned long ifname_compare(const char *_a, const char *_b, const char *_mask)
|
||||
{
|
||||
#ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
|
||||
unsigned long ret = ifname_compare_aligned(_a, _b, _mask);
|
||||
#else
|
||||
unsigned long ret = 0;
|
||||
const u16 *a = (const u16 *)_a;
|
||||
const u16 *b = (const u16 *)_b;
|
||||
const u16 *mask = (const u16 *)_mask;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < IFNAMSIZ/sizeof(u16); i++)
|
||||
ret |= (a[i] ^ b[i]) & mask[i];
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Returns whether packet matches rule or not. */
|
||||
static inline int arp_packet_match(const struct arphdr *arphdr,
|
||||
struct net_device *dev,
|
||||
@@ -83,7 +105,7 @@ static inline int arp_packet_match(const struct arphdr *arphdr,
|
||||
const char *arpptr = (char *)(arphdr + 1);
|
||||
const char *src_devaddr, *tgt_devaddr;
|
||||
__be32 src_ipaddr, tgt_ipaddr;
|
||||
int i, ret;
|
||||
long ret;
|
||||
|
||||
#define FWINV(bool, invflg) ((bool) ^ !!(arpinfo->invflags & (invflg)))
|
||||
|
||||
@@ -156,10 +178,7 @@ static inline int arp_packet_match(const struct arphdr *arphdr,
|
||||
}
|
||||
|
||||
/* Look for ifname matches. */
|
||||
for (i = 0, ret = 0; i < IFNAMSIZ; i++) {
|
||||
ret |= (indev[i] ^ arpinfo->iniface[i])
|
||||
& arpinfo->iniface_mask[i];
|
||||
}
|
||||
ret = ifname_compare(indev, arpinfo->iniface, arpinfo->iniface_mask);
|
||||
|
||||
if (FWINV(ret != 0, ARPT_INV_VIA_IN)) {
|
||||
dprintf("VIA in mismatch (%s vs %s).%s\n",
|
||||
@@ -168,10 +187,7 @@ static inline int arp_packet_match(const struct arphdr *arphdr,
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0, ret = 0; i < IFNAMSIZ; i++) {
|
||||
ret |= (outdev[i] ^ arpinfo->outiface[i])
|
||||
& arpinfo->outiface_mask[i];
|
||||
}
|
||||
ret = ifname_compare(outdev, arpinfo->outiface, arpinfo->outiface_mask);
|
||||
|
||||
if (FWINV(ret != 0, ARPT_INV_VIA_OUT)) {
|
||||
dprintf("VIA out mismatch (%s vs %s).%s\n",
|
||||
@@ -221,7 +237,7 @@ unsigned int arpt_do_table(struct sk_buff *skb,
|
||||
const struct net_device *out,
|
||||
struct xt_table *table)
|
||||
{
|
||||
static const char nulldevname[IFNAMSIZ];
|
||||
static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
|
||||
unsigned int verdict = NF_DROP;
|
||||
const struct arphdr *arp;
|
||||
bool hotdrop = false;
|
||||
@@ -237,9 +253,10 @@ unsigned int arpt_do_table(struct sk_buff *skb,
|
||||
indev = in ? in->name : nulldevname;
|
||||
outdev = out ? out->name : nulldevname;
|
||||
|
||||
read_lock_bh(&table->lock);
|
||||
private = table->private;
|
||||
table_base = (void *)private->entries[smp_processor_id()];
|
||||
rcu_read_lock();
|
||||
private = rcu_dereference(table->private);
|
||||
table_base = rcu_dereference(private->entries[smp_processor_id()]);
|
||||
|
||||
e = get_entry(table_base, private->hook_entry[hook]);
|
||||
back = get_entry(table_base, private->underflow[hook]);
|
||||
|
||||
@@ -311,7 +328,8 @@ unsigned int arpt_do_table(struct sk_buff *skb,
|
||||
e = (void *)e + e->next_offset;
|
||||
}
|
||||
} while (!hotdrop);
|
||||
read_unlock_bh(&table->lock);
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
if (hotdrop)
|
||||
return NF_DROP;
|
||||
@@ -374,7 +392,9 @@ static int mark_source_chains(struct xt_table_info *newinfo,
|
||||
&& unconditional(&e->arp)) || visited) {
|
||||
unsigned int oldpos, size;
|
||||
|
||||
if (t->verdict < -NF_MAX_VERDICT - 1) {
|
||||
if ((strcmp(t->target.u.user.name,
|
||||
ARPT_STANDARD_TARGET) == 0) &&
|
||||
t->verdict < -NF_MAX_VERDICT - 1) {
|
||||
duprintf("mark_source_chains: bad "
|
||||
"negative verdict (%i)\n",
|
||||
t->verdict);
|
||||
@@ -714,11 +734,65 @@ static void get_counters(const struct xt_table_info *t,
|
||||
}
|
||||
}
|
||||
|
||||
static inline struct xt_counters *alloc_counters(struct xt_table *table)
|
||||
|
||||
/* We're lazy, and add to the first CPU; overflow works its fey magic
|
||||
* and everything is OK. */
|
||||
static int
|
||||
add_counter_to_entry(struct arpt_entry *e,
|
||||
const struct xt_counters addme[],
|
||||
unsigned int *i)
|
||||
{
|
||||
ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
|
||||
|
||||
(*i)++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Take values from counters and add them back onto the current cpu */
|
||||
static void put_counters(struct xt_table_info *t,
|
||||
const struct xt_counters counters[])
|
||||
{
|
||||
unsigned int i, cpu;
|
||||
|
||||
local_bh_disable();
|
||||
cpu = smp_processor_id();
|
||||
i = 0;
|
||||
ARPT_ENTRY_ITERATE(t->entries[cpu],
|
||||
t->size,
|
||||
add_counter_to_entry,
|
||||
counters,
|
||||
&i);
|
||||
local_bh_enable();
|
||||
}
|
||||
|
||||
static inline int
|
||||
zero_entry_counter(struct arpt_entry *e, void *arg)
|
||||
{
|
||||
e->counters.bcnt = 0;
|
||||
e->counters.pcnt = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
clone_counters(struct xt_table_info *newinfo, const struct xt_table_info *info)
|
||||
{
|
||||
unsigned int cpu;
|
||||
const void *loc_cpu_entry = info->entries[raw_smp_processor_id()];
|
||||
|
||||
memcpy(newinfo, info, offsetof(struct xt_table_info, entries));
|
||||
for_each_possible_cpu(cpu) {
|
||||
memcpy(newinfo->entries[cpu], loc_cpu_entry, info->size);
|
||||
ARPT_ENTRY_ITERATE(newinfo->entries[cpu], newinfo->size,
|
||||
zero_entry_counter, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static struct xt_counters *alloc_counters(struct xt_table *table)
|
||||
{
|
||||
unsigned int countersize;
|
||||
struct xt_counters *counters;
|
||||
const struct xt_table_info *private = table->private;
|
||||
struct xt_table_info *private = table->private;
|
||||
struct xt_table_info *info;
|
||||
|
||||
/* We need atomic snapshot of counters: rest doesn't change
|
||||
* (other than comefrom, which userspace doesn't care
|
||||
@@ -728,14 +802,30 @@ static inline struct xt_counters *alloc_counters(struct xt_table *table)
|
||||
counters = vmalloc_node(countersize, numa_node_id());
|
||||
|
||||
if (counters == NULL)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
goto nomem;
|
||||
|
||||
/* First, sum counters... */
|
||||
write_lock_bh(&table->lock);
|
||||
get_counters(private, counters);
|
||||
write_unlock_bh(&table->lock);
|
||||
info = xt_alloc_table_info(private->size);
|
||||
if (!info)
|
||||
goto free_counters;
|
||||
|
||||
clone_counters(info, private);
|
||||
|
||||
mutex_lock(&table->lock);
|
||||
xt_table_entry_swap_rcu(private, info);
|
||||
synchronize_net(); /* Wait until smoke has cleared */
|
||||
|
||||
get_counters(info, counters);
|
||||
put_counters(private, counters);
|
||||
mutex_unlock(&table->lock);
|
||||
|
||||
xt_free_table_info(info);
|
||||
|
||||
return counters;
|
||||
|
||||
free_counters:
|
||||
vfree(counters);
|
||||
nomem:
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
static int copy_entries_to_user(unsigned int total_size,
|
||||
@@ -1075,20 +1165,6 @@ static int do_replace(struct net *net, void __user *user, unsigned int len)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* We're lazy, and add to the first CPU; overflow works its fey magic
|
||||
* and everything is OK.
|
||||
*/
|
||||
static inline int add_counter_to_entry(struct arpt_entry *e,
|
||||
const struct xt_counters addme[],
|
||||
unsigned int *i)
|
||||
{
|
||||
|
||||
ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
|
||||
|
||||
(*i)++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_add_counters(struct net *net, void __user *user, unsigned int len,
|
||||
int compat)
|
||||
{
|
||||
@@ -1148,13 +1224,14 @@ static int do_add_counters(struct net *net, void __user *user, unsigned int len,
|
||||
goto free;
|
||||
}
|
||||
|
||||
write_lock_bh(&t->lock);
|
||||
mutex_lock(&t->lock);
|
||||
private = t->private;
|
||||
if (private->number != num_counters) {
|
||||
ret = -EINVAL;
|
||||
goto unlock_up_free;
|
||||
}
|
||||
|
||||
preempt_disable();
|
||||
i = 0;
|
||||
/* Choose the copy that is on our node */
|
||||
loc_cpu_entry = private->entries[smp_processor_id()];
|
||||
@@ -1163,8 +1240,10 @@ static int do_add_counters(struct net *net, void __user *user, unsigned int len,
|
||||
add_counter_to_entry,
|
||||
paddc,
|
||||
&i);
|
||||
preempt_enable();
|
||||
unlock_up_free:
|
||||
write_unlock_bh(&t->lock);
|
||||
mutex_unlock(&t->lock);
|
||||
|
||||
xt_table_unlock(t);
|
||||
module_put(t->me);
|
||||
free:
|
||||
|
||||
@@ -48,8 +48,6 @@ static struct
|
||||
static struct xt_table packet_filter = {
|
||||
.name = "filter",
|
||||
.valid_hooks = FILTER_VALID_HOOKS,
|
||||
.lock = __RW_LOCK_UNLOCKED(packet_filter.lock),
|
||||
.private = NULL,
|
||||
.me = THIS_MODULE,
|
||||
.af = NFPROTO_ARP,
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user