summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/Kconfig1
-rw-r--r--net/Makefile1
-rw-r--r--net/bluetooth/hci_debugfs.c13
-rw-r--r--net/bpf/test_run.c4
-rw-r--r--net/bridge/br_fdb.c2
-rw-r--r--net/caif/caif_socket.c2
-rw-r--r--net/ceph/ceph_common.c4
-rw-r--r--net/core/ethtool.c53
-rw-r--r--net/core/rtnetlink.c49
-rw-r--r--net/core/skbuff.c67
-rw-r--r--net/core/sock.c9
-rw-r--r--net/core/sock_reuseport.c35
-rw-r--r--net/dns_resolver/dns_query.c22
-rw-r--r--net/ipv4/igmp.c4
-rw-r--r--net/ipv4/inet_connection_sock.c1
-rw-r--r--net/ipv4/inet_hashtables.c6
-rw-r--r--net/ipv4/ip_gre.c14
-rw-r--r--net/ipv4/ip_sockglue.c14
-rw-r--r--net/ipv4/netfilter/Kconfig3
-rw-r--r--net/ipv4/netfilter/ipt_CLUSTERIP.c16
-rw-r--r--net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c6
-rw-r--r--net/ipv4/netfilter/nf_flow_table_ipv4.c1
-rw-r--r--net/ipv4/raw.c2
-rw-r--r--net/ipv4/tcp_bbr.c6
-rw-r--r--net/ipv4/tcp_ipv4.c3
-rw-r--r--net/ipv4/tcp_ulp.c59
-rw-r--r--net/ipv6/ip6_gre.c15
-rw-r--r--net/ipv6/ipv6_sockglue.c17
-rw-r--r--net/ipv6/mcast.c8
-rw-r--r--net/ipv6/netfilter/Kconfig3
-rw-r--r--net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c18
-rw-r--r--net/ipv6/netfilter/nf_conntrack_reasm.c1
-rw-r--r--net/ipv6/netfilter/nf_flow_table_ipv6.c1
-rw-r--r--net/ipv6/raw.c2
-rw-r--r--net/ipv6/route.c8
-rw-r--r--net/ipv6/tcp_ipv6.c3
-rw-r--r--net/ipx/Kconfig60
-rw-r--r--net/ipx/Makefile8
-rw-r--r--net/ipx/af_ipx.c2084
-rw-r--r--net/ipx/ipx_proc.c338
-rw-r--r--net/ipx/ipx_route.c293
-rw-r--r--net/ipx/pe2.c36
-rw-r--r--net/ipx/sysctl_net_ipx.c40
-rw-r--r--net/mpls/af_mpls.c24
-rw-r--r--net/netfilter/Kconfig8
-rw-r--r--net/netfilter/ipset/ip_set_hash_ipportnet.c26
-rw-r--r--net/netfilter/ipset/ip_set_hash_net.c9
-rw-r--r--net/netfilter/ipset/ip_set_hash_netiface.c9
-rw-r--r--net/netfilter/ipset/ip_set_hash_netnet.c28
-rw-r--r--net/netfilter/ipset/ip_set_hash_netport.c19
-rw-r--r--net/netfilter/ipset/ip_set_hash_netportnet.c35
-rw-r--r--net/netfilter/nf_flow_table.c76
-rw-r--r--net/netfilter/nf_flow_table_inet.c1
-rw-r--r--net/netfilter/nf_tables_api.c17
-rw-r--r--net/netfilter/nft_flow_offload.c24
-rw-r--r--net/netfilter/x_tables.c16
-rw-r--r--net/netfilter/xt_IDLETIMER.c1
-rw-r--r--net/netfilter/xt_LED.c1
-rw-r--r--net/netfilter/xt_RATEEST.c22
-rw-r--r--net/netfilter/xt_cgroup.c1
-rw-r--r--net/netfilter/xt_limit.c3
-rw-r--r--net/netfilter/xt_nfacct.c1
-rw-r--r--net/netfilter/xt_statistic.c1
-rw-r--r--net/netlink/genetlink.c12
-rw-r--r--net/openvswitch/conntrack.c34
-rw-r--r--net/rds/cong.c2
-rw-r--r--net/rds/connection.c15
-rw-r--r--net/rds/ib.c20
-rw-r--r--net/rds/ib_cm.c1
-rw-r--r--net/rds/rds.h7
-rw-r--r--net/rds/send.c10
-rw-r--r--net/rds/tcp.c42
-rw-r--r--net/rds/tcp_connect.c2
-rw-r--r--net/rds/tcp_recv.c2
-rw-r--r--net/rds/tcp_send.c2
-rw-r--r--net/rds/threads.c6
-rw-r--r--net/rxrpc/conn_client.c3
-rw-r--r--net/rxrpc/conn_event.c1
-rw-r--r--net/rxrpc/conn_object.c16
-rw-r--r--net/rxrpc/rxkad.c92
-rw-r--r--net/sched/act_api.c72
-rw-r--r--net/sched/cls_api.c8
-rw-r--r--net/sched/cls_basic.c33
-rw-r--r--net/sched/cls_bpf.c30
-rw-r--r--net/sched/cls_flower.c34
-rw-r--r--net/sched/cls_u32.c83
-rw-r--r--net/sched/sch_netem.c2
-rw-r--r--net/sched/sch_tbf.c10
-rw-r--r--net/sctp/ipv6.c10
-rw-r--r--net/sctp/protocol.c10
-rw-r--r--net/sctp/sm_make_chunk.c7
-rw-r--r--net/sctp/socket.c10
-rw-r--r--net/sunrpc/sched.c16
-rw-r--r--net/sunrpc/svcsock.c14
-rw-r--r--net/sunrpc/xprt.c3
-rw-r--r--net/sunrpc/xprtrdma/rpc_rdma.c2
-rw-r--r--net/sunrpc/xprtrdma/svc_rdma_backchannel.c5
-rw-r--r--net/sunrpc/xprtrdma/svc_rdma_recvfrom.c9
-rw-r--r--net/sunrpc/xprtrdma/svc_rdma_rw.c12
-rw-r--r--net/sunrpc/xprtrdma/svc_rdma_sendto.c6
-rw-r--r--net/sunrpc/xprtrdma/svc_rdma_transport.c25
-rw-r--r--net/sunrpc/xprtrdma/verbs.c8
-rw-r--r--net/sunrpc/xprtsock.c27
-rw-r--r--net/tipc/msg.c4
-rw-r--r--net/tls/tls_main.c2
-rw-r--r--net/wireless/nl80211.c9
106 files changed, 865 insertions, 3507 deletions
diff --git a/net/Kconfig b/net/Kconfig
index 37ec8e67af57..0428f12c25c2 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -213,7 +213,6 @@ source "net/dsa/Kconfig"
source "net/8021q/Kconfig"
source "net/decnet/Kconfig"
source "net/llc/Kconfig"
-source "net/ipx/Kconfig"
source "drivers/net/appletalk/Kconfig"
source "net/x25/Kconfig"
source "net/lapb/Kconfig"
diff --git a/net/Makefile b/net/Makefile
index 14fede520840..a6147c61b174 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -24,7 +24,6 @@ obj-$(CONFIG_PACKET) += packet/
obj-$(CONFIG_NET_KEY) += key/
obj-$(CONFIG_BRIDGE) += bridge/
obj-$(CONFIG_NET_DSA) += dsa/
-obj-$(CONFIG_IPX) += ipx/
obj-$(CONFIG_ATALK) += appletalk/
obj-$(CONFIG_X25) += x25/
obj-$(CONFIG_LAPB) += lapb/
diff --git a/net/bluetooth/hci_debugfs.c b/net/bluetooth/hci_debugfs.c
index 57403bd567d0..418b76e557b0 100644
--- a/net/bluetooth/hci_debugfs.c
+++ b/net/bluetooth/hci_debugfs.c
@@ -90,19 +90,6 @@ static int __name ## _show(struct seq_file *f, void *ptr) \
\
DEFINE_SHOW_ATTRIBUTE(__name)
-#define DEFINE_SHOW_ATTRIBUTE(__name) \
-static int __name ## _open(struct inode *inode, struct file *file) \
-{ \
- return single_open(file, __name ## _show, inode->i_private); \
-} \
- \
-static const struct file_operations __name ## _fops = { \
- .open = __name ## _open, \
- .read = seq_read, \
- .llseek = seq_lseek, \
- .release = single_release, \
-} \
-
static int features_show(struct seq_file *f, void *ptr)
{
struct hci_dev *hdev = f->private;
diff --git a/net/bpf/test_run.c b/net/bpf/test_run.c
index a86e6687026e..2ced48662c1f 100644
--- a/net/bpf/test_run.c
+++ b/net/bpf/test_run.c
@@ -151,6 +151,7 @@ int bpf_prog_test_run_xdp(struct bpf_prog *prog, const union bpf_attr *kattr,
{
u32 size = kattr->test.data_size_in;
u32 repeat = kattr->test.repeat;
+ struct netdev_rx_queue *rxqueue;
struct xdp_buff xdp = {};
u32 retval, duration;
void *data;
@@ -165,6 +166,9 @@ int bpf_prog_test_run_xdp(struct bpf_prog *prog, const union bpf_attr *kattr,
xdp.data_meta = xdp.data;
xdp.data_end = xdp.data + size;
+ rxqueue = __netif_get_rx_queue(current->nsproxy->net_ns->loopback_dev, 0);
+ xdp.rxq = &rxqueue->xdp_rxq;
+
retval = bpf_test_run(prog, &xdp, repeat, &duration);
if (xdp.data != data + XDP_PACKET_HEADROOM + NET_IP_ALIGN)
size = xdp.data_end - xdp.data;
diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
index dc87fbc9a23b..d9e69e4514be 100644
--- a/net/bridge/br_fdb.c
+++ b/net/bridge/br_fdb.c
@@ -993,7 +993,7 @@ int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
int br_fdb_sync_static(struct net_bridge *br, struct net_bridge_port *p)
{
struct net_bridge_fdb_entry *f, *tmp;
- int err;
+ int err = 0;
ASSERT_RTNL();
diff --git a/net/caif/caif_socket.c b/net/caif/caif_socket.c
index 64048cec41e0..b109445a1df9 100644
--- a/net/caif/caif_socket.c
+++ b/net/caif/caif_socket.c
@@ -1032,6 +1032,8 @@ static int caif_create(struct net *net, struct socket *sock, int protocol,
static struct proto prot = {.name = "PF_CAIF",
.owner = THIS_MODULE,
.obj_size = sizeof(struct caifsock),
+ .useroffset = offsetof(struct caifsock, conn_req.param),
+ .usersize = sizeof_field(struct caifsock, conn_req.param)
};
if (!capable(CAP_SYS_ADMIN) && !capable(CAP_NET_ADMIN))
diff --git a/net/ceph/ceph_common.c b/net/ceph/ceph_common.c
index 5c036d2f401e..1e492ef2a33d 100644
--- a/net/ceph/ceph_common.c
+++ b/net/ceph/ceph_common.c
@@ -421,6 +421,10 @@ ceph_parse_options(char *options, const char *dev_name,
opt->name = kstrndup(argstr[0].from,
argstr[0].to-argstr[0].from,
GFP_KERNEL);
+ if (!opt->name) {
+ err = -ENOMEM;
+ goto out;
+ }
break;
case Opt_secret:
opt->key = kzalloc(sizeof(*opt->key), GFP_KERNEL);
diff --git a/net/core/ethtool.c b/net/core/ethtool.c
index 107b122c8969..494e6a5d7306 100644
--- a/net/core/ethtool.c
+++ b/net/core/ethtool.c
@@ -616,18 +616,15 @@ static int load_link_ksettings_from_user(struct ethtool_link_ksettings *to,
return -EFAULT;
memcpy(&to->base, &link_usettings.base, sizeof(to->base));
- bitmap_from_u32array(to->link_modes.supported,
- __ETHTOOL_LINK_MODE_MASK_NBITS,
- link_usettings.link_modes.supported,
- __ETHTOOL_LINK_MODE_MASK_NU32);
- bitmap_from_u32array(to->link_modes.advertising,
- __ETHTOOL_LINK_MODE_MASK_NBITS,
- link_usettings.link_modes.advertising,
- __ETHTOOL_LINK_MODE_MASK_NU32);
- bitmap_from_u32array(to->link_modes.lp_advertising,
- __ETHTOOL_LINK_MODE_MASK_NBITS,
- link_usettings.link_modes.lp_advertising,
- __ETHTOOL_LINK_MODE_MASK_NU32);
+ bitmap_from_arr32(to->link_modes.supported,
+ link_usettings.link_modes.supported,
+ __ETHTOOL_LINK_MODE_MASK_NBITS);
+ bitmap_from_arr32(to->link_modes.advertising,
+ link_usettings.link_modes.advertising,
+ __ETHTOOL_LINK_MODE_MASK_NBITS);
+ bitmap_from_arr32(to->link_modes.lp_advertising,
+ link_usettings.link_modes.lp_advertising,
+ __ETHTOOL_LINK_MODE_MASK_NBITS);
return 0;
}
@@ -643,18 +640,15 @@ store_link_ksettings_for_user(void __user *to,
struct ethtool_link_usettings link_usettings;
memcpy(&link_usettings.base, &from->base, sizeof(link_usettings));
- bitmap_to_u32array(link_usettings.link_modes.supported,
- __ETHTOOL_LINK_MODE_MASK_NU32,
- from->link_modes.supported,
- __ETHTOOL_LINK_MODE_MASK_NBITS);
- bitmap_to_u32array(link_usettings.link_modes.advertising,
- __ETHTOOL_LINK_MODE_MASK_NU32,
- from->link_modes.advertising,
- __ETHTOOL_LINK_MODE_MASK_NBITS);
- bitmap_to_u32array(link_usettings.link_modes.lp_advertising,
- __ETHTOOL_LINK_MODE_MASK_NU32,
- from->link_modes.lp_advertising,
- __ETHTOOL_LINK_MODE_MASK_NBITS);
+ bitmap_to_arr32(link_usettings.link_modes.supported,
+ from->link_modes.supported,
+ __ETHTOOL_LINK_MODE_MASK_NBITS);
+ bitmap_to_arr32(link_usettings.link_modes.advertising,
+ from->link_modes.advertising,
+ __ETHTOOL_LINK_MODE_MASK_NBITS);
+ bitmap_to_arr32(link_usettings.link_modes.lp_advertising,
+ from->link_modes.lp_advertising,
+ __ETHTOOL_LINK_MODE_MASK_NBITS);
if (copy_to_user(to, &link_usettings, sizeof(link_usettings)))
return -EFAULT;
@@ -2358,10 +2352,8 @@ static int ethtool_get_per_queue_coalesce(struct net_device *dev,
useraddr += sizeof(*per_queue_opt);
- bitmap_from_u32array(queue_mask,
- MAX_NUM_QUEUE,
- per_queue_opt->queue_mask,
- DIV_ROUND_UP(MAX_NUM_QUEUE, 32));
+ bitmap_from_arr32(queue_mask, per_queue_opt->queue_mask,
+ MAX_NUM_QUEUE);
for_each_set_bit(bit, queue_mask, MAX_NUM_QUEUE) {
struct ethtool_coalesce coalesce = { .cmd = ETHTOOL_GCOALESCE };
@@ -2393,10 +2385,7 @@ static int ethtool_set_per_queue_coalesce(struct net_device *dev,
useraddr += sizeof(*per_queue_opt);
- bitmap_from_u32array(queue_mask,
- MAX_NUM_QUEUE,
- per_queue_opt->queue_mask,
- DIV_ROUND_UP(MAX_NUM_QUEUE, 32));
+ bitmap_from_arr32(queue_mask, per_queue_opt->queue_mask, MAX_NUM_QUEUE);
n_queue = bitmap_weight(queue_mask, MAX_NUM_QUEUE);
tmp = backup = kmalloc_array(n_queue, sizeof(*backup), GFP_KERNEL);
if (!backup)
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 204297dffd2a..bc290413a49d 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -1951,6 +1951,38 @@ static struct net *rtnl_link_get_net_capable(const struct sk_buff *skb,
return net;
}
+/* Verify that rtnetlink requests do not pass additional properties
+ * potentially referring to different network namespaces.
+ */
+static int rtnl_ensure_unique_netns(struct nlattr *tb[],
+ struct netlink_ext_ack *extack,
+ bool netns_id_only)
+{
+
+ if (netns_id_only) {
+ if (!tb[IFLA_NET_NS_PID] && !tb[IFLA_NET_NS_FD])
+ return 0;
+
+ NL_SET_ERR_MSG(extack, "specified netns attribute not supported");
+ return -EOPNOTSUPP;
+ }
+
+ if (tb[IFLA_IF_NETNSID] && (tb[IFLA_NET_NS_PID] || tb[IFLA_NET_NS_FD]))
+ goto invalid_attr;
+
+ if (tb[IFLA_NET_NS_PID] && (tb[IFLA_IF_NETNSID] || tb[IFLA_NET_NS_FD]))
+ goto invalid_attr;
+
+ if (tb[IFLA_NET_NS_FD] && (tb[IFLA_IF_NETNSID] || tb[IFLA_NET_NS_PID]))
+ goto invalid_attr;
+
+ return 0;
+
+invalid_attr:
+ NL_SET_ERR_MSG(extack, "multiple netns identifying attributes specified");
+ return -EINVAL;
+}
+
static int validate_linkmsg(struct net_device *dev, struct nlattr *tb[])
{
if (dev) {
@@ -2553,6 +2585,10 @@ static int rtnl_setlink(struct sk_buff *skb, struct nlmsghdr *nlh,
if (err < 0)
goto errout;
+ err = rtnl_ensure_unique_netns(tb, extack, false);
+ if (err < 0)
+ goto errout;
+
if (tb[IFLA_IFNAME])
nla_strlcpy(ifname, tb[IFLA_IFNAME], IFNAMSIZ);
else
@@ -2649,6 +2685,10 @@ static int rtnl_dellink(struct sk_buff *skb, struct nlmsghdr *nlh,
if (err < 0)
return err;
+ err = rtnl_ensure_unique_netns(tb, extack, true);
+ if (err < 0)
+ return err;
+
if (tb[IFLA_IFNAME])
nla_strlcpy(ifname, tb[IFLA_IFNAME], IFNAMSIZ);
@@ -2802,8 +2842,9 @@ replay:
if (err < 0)
return err;
- if (tb[IFLA_IF_NETNSID])
- return -EOPNOTSUPP;
+ err = rtnl_ensure_unique_netns(tb, extack, false);
+ if (err < 0)
+ return err;
if (tb[IFLA_IFNAME])
nla_strlcpy(ifname, tb[IFLA_IFNAME], IFNAMSIZ);
@@ -3048,6 +3089,10 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr *nlh,
if (err < 0)
return err;
+ err = rtnl_ensure_unique_netns(tb, extack, true);
+ if (err < 0)
+ return err;
+
if (tb[IFLA_IF_NETNSID]) {
netnsid = nla_get_s32(tb[IFLA_IF_NETNSID]);
tgt_net = get_target_net(NETLINK_CB(skb).sk, netnsid);
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 01e8285aea73..09bd89c90a71 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -3894,10 +3894,12 @@ EXPORT_SYMBOL_GPL(skb_gro_receive);
void __init skb_init(void)
{
- skbuff_head_cache = kmem_cache_create("skbuff_head_cache",
+ skbuff_head_cache = kmem_cache_create_usercopy("skbuff_head_cache",
sizeof(struct sk_buff),
0,
SLAB_HWCACHE_ALIGN|SLAB_PANIC,
+ offsetof(struct sk_buff, cb),
+ sizeof_field(struct sk_buff, cb),
NULL);
skbuff_fclone_cache = kmem_cache_create("skbuff_fclone_cache",
sizeof(struct sk_buff_fclones),
@@ -4914,37 +4916,74 @@ unsigned int skb_gso_transport_seglen(const struct sk_buff *skb)
EXPORT_SYMBOL_GPL(skb_gso_transport_seglen);
/**
- * skb_gso_validate_mtu - Return in case such skb fits a given MTU
+ * skb_gso_size_check - check the skb size, considering GSO_BY_FRAGS
*
- * @skb: GSO skb
- * @mtu: MTU to validate against
+ * There are a couple of instances where we have a GSO skb, and we
+ * want to determine what size it would be after it is segmented.
*
- * skb_gso_validate_mtu validates if a given skb will fit a wanted MTU
- * once split.
+ * We might want to check:
+ * - L3+L4+payload size (e.g. IP forwarding)
+ * - L2+L3+L4+payload size (e.g. sanity check before passing to driver)
+ *
+ * This is a helper to do that correctly considering GSO_BY_FRAGS.
+ *
+ * @seg_len: The segmented length (from skb_gso_*_seglen). In the
+ * GSO_BY_FRAGS case this will be [header sizes + GSO_BY_FRAGS].
+ *
+ * @max_len: The maximum permissible length.
+ *
+ * Returns true if the segmented length <= max length.
*/
-bool skb_gso_validate_mtu(const struct sk_buff *skb, unsigned int mtu)
-{
+static inline bool skb_gso_size_check(const struct sk_buff *skb,
+ unsigned int seg_len,
+ unsigned int max_len) {
const struct skb_shared_info *shinfo = skb_shinfo(skb);
const struct sk_buff *iter;
- unsigned int hlen;
-
- hlen = skb_gso_network_seglen(skb);
if (shinfo->gso_size != GSO_BY_FRAGS)
- return hlen <= mtu;
+ return seg_len <= max_len;
/* Undo this so we can re-use header sizes */
- hlen -= GSO_BY_FRAGS;
+ seg_len -= GSO_BY_FRAGS;
skb_walk_frags(skb, iter) {
- if (hlen + skb_headlen(iter) > mtu)
+ if (seg_len + skb_headlen(iter) > max_len)
return false;
}
return true;
}
+
+/**
+ * skb_gso_validate_mtu - Return in case such skb fits a given MTU
+ *
+ * @skb: GSO skb
+ * @mtu: MTU to validate against
+ *
+ * skb_gso_validate_mtu validates if a given skb will fit a wanted MTU
+ * once split.
+ */
+bool skb_gso_validate_mtu(const struct sk_buff *skb, unsigned int mtu)
+{
+ return skb_gso_size_check(skb, skb_gso_network_seglen(skb), mtu);
+}
EXPORT_SYMBOL_GPL(skb_gso_validate_mtu);
+/**
+ * skb_gso_validate_mac_len - Will a split GSO skb fit in a given length?
+ *
+ * @skb: GSO skb
+ * @len: length to validate against
+ *
+ * skb_gso_validate_mac_len validates if a given skb will fit a wanted
+ * length once split, including L2, L3 and L4 headers and the payload.
+ */
+bool skb_gso_validate_mac_len(const struct sk_buff *skb, unsigned int len)
+{
+ return skb_gso_size_check(skb, skb_gso_mac_seglen(skb), len);
+}
+EXPORT_SYMBOL_GPL(skb_gso_validate_mac_len);
+
static struct sk_buff *skb_reorder_vlan_header(struct sk_buff *skb)
{
if (skb_cow(skb, skb_headroom(skb)) < 0) {
diff --git a/net/core/sock.c b/net/core/sock.c
index 1033f8ab0547..b026e1717df4 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -1683,16 +1683,13 @@ struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority)
newsk->sk_dst_pending_confirm = 0;
newsk->sk_wmem_queued = 0;
newsk->sk_forward_alloc = 0;
-
- /* sk->sk_memcg will be populated at accept() time */
- newsk->sk_memcg = NULL;
-
atomic_set(&newsk->sk_drops, 0);
newsk->sk_send_head = NULL;
newsk->sk_userlocks = sk->sk_userlocks & ~SOCK_BINDPORT_LOCK;
atomic_set(&newsk->sk_zckey, 0);
sock_reset_flag(newsk, SOCK_DONE);
+ mem_cgroup_sk_alloc(newsk);
cgroup_sk_alloc(&newsk->sk_cgrp_data);
rcu_read_lock();
@@ -3194,8 +3191,10 @@ static int req_prot_init(const struct proto *prot)
int proto_register(struct proto *prot, int alloc_slab)
{
if (alloc_slab) {
- prot->slab = kmem_cache_create(prot->name, prot->obj_size, 0,
+ prot->slab = kmem_cache_create_usercopy(prot->name,
+ prot->obj_size, 0,
SLAB_HWCACHE_ALIGN | prot->slab_flags,
+ prot->useroffset, prot->usersize,
NULL);
if (prot->slab == NULL) {
diff --git a/net/core/sock_reuseport.c b/net/core/sock_reuseport.c
index c5bb52bc73a1..064acb04be0f 100644
--- a/net/core/sock_reuseport.c
+++ b/net/core/sock_reuseport.c
@@ -94,6 +94,16 @@ static struct sock_reuseport *reuseport_grow(struct sock_reuseport *reuse)
return more_reuse;
}
+static void reuseport_free_rcu(struct rcu_head *head)
+{
+ struct sock_reuseport *reuse;
+
+ reuse = container_of(head, struct sock_reuseport, rcu);
+ if (reuse->prog)
+ bpf_prog_destroy(reuse->prog);
+ kfree(reuse);
+}
+
/**
* reuseport_add_sock - Add a socket to the reuseport group of another.
* @sk: New socket to add to the group.
@@ -102,7 +112,7 @@ static struct sock_reuseport *reuseport_grow(struct sock_reuseport *reuse)
*/
int reuseport_add_sock(struct sock *sk, struct sock *sk2)
{
- struct sock_reuseport *reuse;
+ struct sock_reuseport *old_reuse, *reuse;
if (!rcu_access_pointer(sk2->sk_reuseport_cb)) {
int err = reuseport_alloc(sk2);
@@ -113,10 +123,13 @@ int reuseport_add_sock(struct sock *sk, struct sock *sk2)
spin_lock_bh(&reuseport_lock);
reuse = rcu_dereference_protected(sk2->sk_reuseport_cb,
- lockdep_is_held(&reuseport_lock)),
- WARN_ONCE(rcu_dereference_protected(sk->sk_reuseport_cb,
- lockdep_is_held(&reuseport_lock)),
- "socket already in reuseport group");
+ lockdep_is_held(&reuseport_lock));
+ old_reuse = rcu_dereference_protected(sk->sk_reuseport_cb,
+ lockdep_is_held(&reuseport_lock));
+ if (old_reuse && old_reuse->num_socks != 1) {
+ spin_unlock_bh(&reuseport_lock);
+ return -EBUSY;
+ }
if (reuse->num_socks == reuse->max_socks) {
reuse = reuseport_grow(reuse);
@@ -134,19 +147,11 @@ int reuseport_add_sock(struct sock *sk, struct sock *sk2)
spin_unlock_bh(&reuseport_lock);
+ if (old_reuse)
+ call_rcu(&old_reuse->rcu, reuseport_free_rcu);
return 0;
}
-static void reuseport_free_rcu(struct rcu_head *head)
-{
- struct sock_reuseport *reuse;
-
- reuse = container_of(head, struct sock_reuseport, rcu);
- if (reuse->prog)
- bpf_prog_destroy(reuse->prog);
- kfree(reuse);
-}
-
void reuseport_detach_sock(struct sock *sk)
{
struct sock_reuseport *reuse;
diff --git a/net/dns_resolver/dns_query.c b/net/dns_resolver/dns_query.c
index af781010753b..49da67034f29 100644
--- a/net/dns_resolver/dns_query.c
+++ b/net/dns_resolver/dns_query.c
@@ -52,11 +52,11 @@
* @name: Name to look up
* @namelen: Length of name
* @options: Request options (or NULL if no options)
- * @_result: Where to place the returned data.
+ * @_result: Where to place the returned data (or NULL)
* @_expiry: Where to store the result expiry time (or NULL)
*
- * The data will be returned in the pointer at *result, and the caller is
- * responsible for freeing it.
+ * The data will be returned in the pointer at *result, if provided, and the
+ * caller is responsible for freeing it.
*
* The description should be of the form "[<query_type>:]<domain_name>", and
* the options need to be appropriate for the query type requested. If no
@@ -81,7 +81,7 @@ int dns_query(const char *type, const char *name, size_t namelen,
kenter("%s,%*.*s,%zu,%s",
type, (int)namelen, (int)namelen, name, namelen, options);
- if (!name || namelen == 0 || !_result)
+ if (!name || namelen == 0)
return -EINVAL;
/* construct the query key description as "[<type>:]<name>" */
@@ -146,13 +146,15 @@ int dns_query(const char *type, const char *name, size_t namelen,
upayload = user_key_payload_locked(rkey);
len = upayload->datalen;
- ret = -ENOMEM;
- *_result = kmalloc(len + 1, GFP_KERNEL);
- if (!*_result)
- goto put;
+ if (_result) {
+ ret = -ENOMEM;
+ *_result = kmalloc(len + 1, GFP_KERNEL);
+ if (!*_result)
+ goto put;
- memcpy(*_result, upayload->data, len);
- (*_result)[len] = '\0';
+ memcpy(*_result, upayload->data, len);
+ (*_result)[len] = '\0';
+ }
if (_expiry)
*_expiry = rkey->expiry;
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index 10f7f74a0831..f2402581fef1 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -386,7 +386,11 @@ static struct sk_buff *igmpv3_newpack(struct net_device *dev, unsigned int mtu)
pip->frag_off = htons(IP_DF);
pip->ttl = 1;
pip->daddr = fl4.daddr;
+
+ rcu_read_lock();
pip->saddr = igmpv3_get_srcaddr(dev, &fl4);
+ rcu_read_unlock();
+
pip->protocol = IPPROTO_IGMP;
pip->tot_len = 0; /* filled in later */
ip_select_ident(net, skb, NULL);
diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c
index 12410ec6f7f7..881ac6d046f2 100644
--- a/net/ipv4/inet_connection_sock.c
+++ b/net/ipv4/inet_connection_sock.c
@@ -475,7 +475,6 @@ struct sock *inet_csk_accept(struct sock *sk, int flags, int *err, bool kern)
}
spin_unlock_bh(&queue->fastopenq.lock);
}
- mem_cgroup_sk_alloc(newsk);
out:
release_sock(sk);
if (req)
diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c
index 37b7da0b975d..31ff46daae97 100644
--- a/net/ipv4/inet_hashtables.c
+++ b/net/ipv4/inet_hashtables.c
@@ -625,9 +625,8 @@ EXPORT_SYMBOL_GPL(inet_hash);
void inet_unhash(struct sock *sk)
{
struct inet_hashinfo *hashinfo = sk->sk_prot->h.hashinfo;
- struct inet_listen_hashbucket *ilb;
+ struct inet_listen_hashbucket *ilb = NULL;
spinlock_t *lock;
- bool listener = false;
if (sk_unhashed(sk))
return;
@@ -635,7 +634,6 @@ void inet_unhash(struct sock *sk)
if (sk->sk_state == TCP_LISTEN) {
ilb = &hashinfo->listening_hash[inet_sk_listen_hashfn(sk)];
lock = &ilb->lock;
- listener = true;
} else {
lock = inet_ehash_lockp(hashinfo, sk->sk_hash);
}
@@ -645,7 +643,7 @@ void inet_unhash(struct sock *sk)
if (rcu_access_pointer(sk->sk_reuseport_cb))
reuseport_detach_sock(sk);
- if (listener) {
+ if (ilb) {
inet_unhash2(hashinfo, sk);
__sk_del_node_init(sk);
ilb->count--;
diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
index 6ec670fbbbdd..45d97e9b2759 100644
--- a/net/ipv4/ip_gre.c
+++ b/net/ipv4/ip_gre.c
@@ -261,6 +261,7 @@ static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi,
struct ip_tunnel_net *itn;
struct ip_tunnel *tunnel;
const struct iphdr *iph;
+ struct erspan_md2 *md2;
int ver;
int len;
@@ -313,21 +314,14 @@ static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi,
return PACKET_REJECT;
md = ip_tunnel_info_opts(&tun_dst->u.tun_info);
- memcpy(md, pkt_md, sizeof(*md));
md->version = ver;
+ md2 = &md->u.md2;
+ memcpy(md2, pkt_md, ver == 1 ? ERSPAN_V1_MDSIZE :
+ ERSPAN_V2_MDSIZE);
info = &tun_dst->u.tun_info;
info->key.tun_flags |= TUNNEL_ERSPAN_OPT;
info->options_len = sizeof(*md);
- } else {
- tunnel->erspan_ver = ver;
- if (ver == 1) {
- tunnel->index = ntohl(pkt_md->u.index);
- } else {
- tunnel->dir = pkt_md->u.md2.dir;
- tunnel->hwid = get_hwid(&pkt_md->u.md2);
- }
-
}
skb_reset_mac_header(skb);
diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c
index 6cc70fa488cb..008be04ac1cc 100644
--- a/net/ipv4/ip_sockglue.c
+++ b/net/ipv4/ip_sockglue.c
@@ -1255,11 +1255,8 @@ int ip_setsockopt(struct sock *sk, int level,
if (err == -ENOPROTOOPT && optname != IP_HDRINCL &&
optname != IP_IPSEC_POLICY &&
optname != IP_XFRM_POLICY &&
- !ip_mroute_opt(optname)) {
- lock_sock(sk);
+ !ip_mroute_opt(optname))
err = nf_setsockopt(sk, PF_INET, optname, optval, optlen);
- release_sock(sk);
- }
#endif
return err;
}
@@ -1284,12 +1281,9 @@ int compat_ip_setsockopt(struct sock *sk, int level, int optname,
if (err == -ENOPROTOOPT && optname != IP_HDRINCL &&
optname != IP_IPSEC_POLICY &&
optname != IP_XFRM_POLICY &&
- !ip_mroute_opt(optname)) {
- lock_sock(sk);
- err = compat_nf_setsockopt(sk, PF_INET, optname,
- optval, optlen);
- release_sock(sk);
- }
+ !ip_mroute_opt(optname))
+ err = compat_nf_setsockopt(sk, PF_INET, optname, optval,
+ optlen);
#endif
return err;
}
diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig
index 5f52236780b4..dfe6fa4ea554 100644
--- a/net/ipv4/netfilter/Kconfig
+++ b/net/ipv4/netfilter/Kconfig
@@ -80,8 +80,7 @@ endif # NF_TABLES
config NF_FLOW_TABLE_IPV4
tristate "Netfilter flow table IPv4 module"
- depends on NF_CONNTRACK && NF_TABLES
- select NF_FLOW_TABLE
+ depends on NF_FLOW_TABLE
help
This option adds the flow table IPv4 support.
diff --git a/net/ipv4/netfilter/ipt_CLUSTERIP.c b/net/ipv4/netfilter/ipt_CLUSTERIP.c
index c29a6ca6c6d6..3a84a60f6b39 100644
--- a/net/ipv4/netfilter/ipt_CLUSTERIP.c
+++ b/net/ipv4/netfilter/ipt_CLUSTERIP.c
@@ -431,7 +431,7 @@ static int clusterip_tg_check(const struct xt_tgchk_param *par)
struct ipt_clusterip_tgt_info *cipinfo = par->targinfo;
const struct ipt_entry *e = par->entryinfo;
struct clusterip_config *config;
- int ret;
+ int ret, i;
if (par->nft_compat) {
pr_err("cannot use CLUSTERIP target from nftables compat\n");
@@ -450,8 +450,18 @@ static int clusterip_tg_check(const struct xt_tgchk_param *par)
pr_info("Please specify destination IP\n");
return -EINVAL;
}
-
- /* FIXME: further sanity checks */
+ if (cipinfo->num_local_nodes > ARRAY_SIZE(cipinfo->local_nodes)) {
+ pr_info("bad num_local_nodes %u\n", cipinfo->num_local_nodes);
+ return -EINVAL;
+ }
+ for (i = 0; i < cipinfo->num_local_nodes; i++) {
+ if (cipinfo->local_nodes[i] - 1 >=
+ sizeof(config->local_nodes) * 8) {
+ pr_info("bad local_nodes[%d] %u\n",
+ i, cipinfo->local_nodes[i]);
+ return -EINVAL;
+ }
+ }
config = clusterip_config_find_get(par->net, e->ip.dst.s_addr, 1);
if (!config) {
diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
index de213a397ea8..b50721d9d30e 100644
--- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
+++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
@@ -213,15 +213,19 @@ getorigdst(struct sock *sk, int optval, void __user *user, int *len)
struct nf_conntrack_tuple tuple;
memset(&tuple, 0, sizeof(tuple));
+
+ lock_sock(sk);
tuple.src.u3.ip = inet->inet_rcv_saddr;
tuple.src.u.tcp.port = inet->inet_sport;
tuple.dst.u3.ip = inet->inet_daddr;
tuple.dst.u.tcp.port = inet->inet_dport;
tuple.src.l3num = PF_INET;
tuple.dst.protonum = sk->sk_protocol;
+ release_sock(sk);
/* We only do TCP and SCTP at the moment: is there a better way? */
- if (sk->sk_protocol != IPPROTO_TCP && sk->sk_protocol != IPPROTO_SCTP) {
+ if (tuple.dst.protonum != IPPROTO_TCP &&
+ tuple.dst.protonum != IPPROTO_SCTP) {
pr_debug("SO_ORIGINAL_DST: Not a TCP/SCTP socket\n");
return -ENOPROTOOPT;
}
diff --git a/net/ipv4/netfilter/nf_flow_table_ipv4.c b/net/ipv4/netfilter/nf_flow_table_ipv4.c
index b2d01eb25f2c..25d2975da156 100644
--- a/net/ipv4/netfilter/nf_flow_table_ipv4.c
+++ b/net/ipv4/netfilter/nf_flow_table_ipv4.c
@@ -260,6 +260,7 @@ static struct nf_flowtable_type flowtable_ipv4 = {
.family = NFPROTO_IPV4,
.params = &nf_flow_offload_rhash_params,
.gc = nf_flow_offload_work_gc,
+ .free = nf_flow_table_free,
.hook = nf_flow_offload_ip_hook,
.owner = THIS_MODULE,
};
diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c
index 7c509697ebc7..9b367fc48d7d 100644
--- a/net/ipv4/raw.c
+++ b/net/ipv4/raw.c
@@ -990,6 +990,8 @@ struct proto raw_prot = {
.hash = raw_hash_sk,
.unhash = raw_unhash_sk,
.obj_size = sizeof(struct raw_sock),
+ .useroffset = offsetof(struct raw_sock, filter),
+ .usersize = sizeof_field(struct raw_sock, filter),
.h.raw_hash = &raw_v4_hashinfo,
#ifdef CONFIG_COMPAT
.compat_setsockopt = compat_raw_setsockopt,
diff --git a/net/ipv4/tcp_bbr.c b/net/ipv4/tcp_bbr.c
index 785712be5b0d..a471f696e13c 100644
--- a/net/ipv4/tcp_bbr.c
+++ b/net/ipv4/tcp_bbr.c
@@ -481,7 +481,8 @@ static void bbr_advance_cycle_phase(struct sock *sk)
bbr->cycle_idx = (bbr->cycle_idx + 1) & (CYCLE_LEN - 1);
bbr->cycle_mstamp = tp->delivered_mstamp;
- bbr->pacing_gain = bbr_pacing_gain[bbr->cycle_idx];
+ bbr->pacing_gain = bbr->lt_use_bw ? BBR_UNIT :
+ bbr_pacing_gain[bbr->cycle_idx];
}
/* Gain cycling: cycle pacing gain to converge to fair share of available bw. */
@@ -490,8 +491,7 @@ static void bbr_update_cycle_phase(struct sock *sk,
{
struct bbr *bbr = inet_csk_ca(sk);
- if ((bbr->mode == BBR_PROBE_BW) && !bbr->lt_use_bw &&
- bbr_is_next_cycle_phase(sk, rs))
+ if (bbr->mode == BBR_PROBE_BW && bbr_is_next_cycle_phase(sk, rs))
bbr_advance_cycle_phase(sk);
}
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 95738aa0d8a6..f8ad397e285e 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -705,7 +705,8 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb)
*/
if (sk) {
arg.bound_dev_if = sk->sk_bound_dev_if;
- trace_tcp_send_reset(sk, skb);
+ if (sk_fullsock(sk))
+ trace_tcp_send_reset(sk, skb);
}
BUILD_BUG_ON(offsetof(struct sock, sk_bound_dev_if) !=
diff --git a/net/ipv4/tcp_ulp.c b/net/ipv4/tcp_ulp.c
index 6bb9e14c710a..622caa4039e0 100644
--- a/net/ipv4/tcp_ulp.c
+++ b/net/ipv4/tcp_ulp.c
@@ -29,6 +29,18 @@ static struct tcp_ulp_ops *tcp_ulp_find(const char *name)
return NULL;
}
+static struct tcp_ulp_ops *tcp_ulp_find_id(const int ulp)
+{
+ struct tcp_ulp_ops *e;
+
+ list_for_each_entry_rcu(e, &tcp_ulp_list, list) {
+ if (e->uid == ulp)
+ return e;
+ }
+
+ return NULL;
+}
+
static const struct tcp_ulp_ops *__tcp_ulp_find_autoload(const char *name)
{
const struct tcp_ulp_ops *ulp = NULL;
@@ -51,6 +63,18 @@ static const struct tcp_ulp_ops *__tcp_ulp_find_autoload(const char *name)
return ulp;
}
+static const struct tcp_ulp_ops *__tcp_ulp_lookup(const int uid)
+{
+ const struct tcp_ulp_ops *ulp;
+
+ rcu_read_lock();
+ ulp = tcp_ulp_find_id(uid);
+ if (!ulp || !try_module_get(ulp->owner))
+ ulp = NULL;
+ rcu_read_unlock();
+ return ulp;
+}
+
/* Attach new upper layer protocol to the list
* of available protocols.
*/
@@ -59,13 +83,10 @@ int tcp_register_ulp(struct tcp_ulp_ops *ulp)
int ret = 0;
spin_lock(&tcp_ulp_list_lock);
- if (tcp_ulp_find(ulp->name)) {
- pr_notice("%s already registered or non-unique name\n",
- ulp->name);
+ if (tcp_ulp_find(ulp->name))
ret = -EEXIST;
- } else {
+ else
list_add_tail_rcu(&ulp->list, &tcp_ulp_list);
- }
spin_unlock(&tcp_ulp_list_lock);
return ret;
@@ -124,6 +145,34 @@ int tcp_set_ulp(struct sock *sk, const char *name)
if (!ulp_ops)
return -ENOENT;
+ if (!ulp_ops->user_visible) {
+ module_put(ulp_ops->owner);
+ return -ENOENT;
+ }
+
+ err = ulp_ops->init(sk);
+ if (err) {
+ module_put(ulp_ops->owner);
+ return err;
+ }
+
+ icsk->icsk_ulp_ops = ulp_ops;
+ return 0;
+}
+
+int tcp_set_ulp_id(struct sock *sk, int ulp)
+{
+ struct inet_connection_sock *icsk = inet_csk(sk);
+ const struct tcp_ulp_ops *ulp_ops;
+ int err;
+
+ if (icsk->icsk_ulp_ops)
+ return -EEXIST;
+
+ ulp_ops = __tcp_ulp_lookup(ulp);
+ if (!ulp_ops)
+ return -ENOENT;
+
err = ulp_ops->init(sk);
if (err) {
module_put(ulp_ops->owner);
diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
index 05f070e123e4..3c353125546d 100644
--- a/net/ipv6/ip6_gre.c
+++ b/net/ipv6/ip6_gre.c
@@ -505,6 +505,7 @@ static int ip6erspan_rcv(struct sk_buff *skb, int gre_hdr_len,
struct erspan_base_hdr *ershdr;
struct erspan_metadata *pkt_md;
const struct ipv6hdr *ipv6h;
+ struct erspan_md2 *md2;
struct ip6_tnl *tunnel;
u8 ver;
@@ -551,24 +552,16 @@ static int ip6erspan_rcv(struct sk_buff *skb, int gre_hdr_len,
info = &tun_dst->u.tun_info;
md = ip_tunnel_info_opts(info);
-
- memcpy(md, pkt_md, sizeof(*md));
md->version = ver;
+ md2 = &md->u.md2;
+ memcpy(md2, pkt_md, ver == 1 ? ERSPAN_V1_MDSIZE :
+ ERSPAN_V2_MDSIZE);
info->key.tun_flags |= TUNNEL_ERSPAN_OPT;
info->options_len = sizeof(*md);
ip6_tnl_rcv(tunnel, skb, tpi, tun_dst, log_ecn_error);
} else {
- tunnel->parms.erspan_ver = ver;
-
- if (ver == 1) {
- tunnel->parms.index = ntohl(pkt_md->u.index);
- } else {
- tunnel->parms.dir = pkt_md->u.md2.dir;
- tunnel->parms.hwid = get_hwid(&pkt_md->u.md2);
- }
-
ip6_tnl_rcv(tunnel, skb, tpi, NULL, log_ecn_error);
}
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index e8ffb5b5d84e..d78d41fc4b1a 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -923,12 +923,8 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname,
#ifdef CONFIG_NETFILTER
/* we need to exclude all possible ENOPROTOOPTs except default case */
if (err == -ENOPROTOOPT && optname != IPV6_IPSEC_POLICY &&
- optname != IPV6_XFRM_POLICY) {
- lock_sock(sk);
- err = nf_setsockopt(sk, PF_INET6, optname, optval,
- optlen);
- release_sock(sk);
- }
+ optname != IPV6_XFRM_POLICY)
+ err = nf_setsockopt(sk, PF_INET6, optname, optval, optlen);
#endif
return err;
}
@@ -958,12 +954,9 @@ int compat_ipv6_setsockopt(struct sock *sk, int level, int optname,
#ifdef CONFIG_NETFILTER
/* we need to exclude all possible ENOPROTOOPTs except default case */
if (err == -ENOPROTOOPT && optname != IPV6_IPSEC_POLICY &&
- optname != IPV6_XFRM_POLICY) {
- lock_sock(sk);
- err = compat_nf_setsockopt(sk, PF_INET6, optname,
- optval, optlen);
- release_sock(sk);
- }
+ optname != IPV6_XFRM_POLICY)
+ err = compat_nf_setsockopt(sk, PF_INET6, optname, optval,
+ optlen);
#endif
return err;
}
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index 6a5d0e39bb87..9b9d2ff01b35 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -65,10 +65,10 @@
#include <net/ip6_checksum.h>
/* Ensure that we have struct in6_addr aligned on 32bit word. */
-static void *__mld2_query_bugs[] __attribute__((__unused__)) = {
- BUILD_BUG_ON_NULL(offsetof(struct mld2_query, mld2q_srcs) % 4),
- BUILD_BUG_ON_NULL(offsetof(struct mld2_report, mld2r_grec) % 4),
- BUILD_BUG_ON_NULL(offsetof(struct mld2_grec, grec_mca) % 4)
+static int __mld2_query_bugs[] __attribute__((__unused__)) = {
+ BUILD_BUG_ON_ZERO(offsetof(struct mld2_query, mld2q_srcs) % 4),
+ BUILD_BUG_ON_ZERO(offsetof(struct mld2_report, mld2r_grec) % 4),
+ BUILD_BUG_ON_ZERO(offsetof(struct mld2_grec, grec_mca) % 4)
};
static struct in6_addr mld2_all_mcr = MLD2_ALL_MCR_INIT;
diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig
index 4a634b7a2c80..d395d1590699 100644
--- a/net/ipv6/netfilter/Kconfig
+++ b/net/ipv6/netfilter/Kconfig
@@ -73,8 +73,7 @@ endif # NF_TABLES
config NF_FLOW_TABLE_IPV6
tristate "Netfilter flow table IPv6 module"
- depends on NF_CONNTRACK && NF_TABLES
- select NF_FLOW_TABLE
+ depends on NF_FLOW_TABLE
help
This option adds the flow table IPv6 support.
diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
index 11a313fd9273..663827ee3cf8 100644
--- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
+++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
@@ -221,20 +221,27 @@ static const struct nf_hook_ops ipv6_conntrack_ops[] = {
static int
ipv6_getorigdst(struct sock *sk, int optval, void __user *user, int *len)
{
- const struct inet_sock *inet = inet_sk(sk);
+ struct nf_conntrack_tuple tuple = { .src.l3num = NFPROTO_IPV6 };
const struct ipv6_pinfo *inet6 = inet6_sk(sk);
+ const struct inet_sock *inet = inet_sk(sk);
const struct nf_conntrack_tuple_hash *h;
struct sockaddr_in6 sin6;
- struct nf_conntrack_tuple tuple = { .src.l3num = NFPROTO_IPV6 };
struct nf_conn *ct;
+ __be32 flow_label;
+ int bound_dev_if;
+ lock_sock(sk);
tuple.src.u3.in6 = sk->sk_v6_rcv_saddr;
tuple.src.u.tcp.port = inet->inet_sport;
tuple.dst.u3.in6 = sk->sk_v6_daddr;
tuple.dst.u.tcp.port = inet->inet_dport;
tuple.dst.protonum = sk->sk_protocol;
+ bound_dev_if = sk->sk_bound_dev_if;
+ flow_label = inet6->flow_label;
+ release_sock(sk);
- if (sk->sk_protocol != IPPROTO_TCP && sk->sk_protocol != IPPROTO_SCTP)
+ if (tuple.dst.protonum != IPPROTO_TCP &&
+ tuple.dst.protonum != IPPROTO_SCTP)
return -ENOPROTOOPT;
if (*len < 0 || (unsigned int) *len < sizeof(sin6))
@@ -252,14 +259,13 @@ ipv6_getorigdst(struct sock *sk, int optval, void __user *user, int *len)
sin6.sin6_family = AF_INET6;
sin6.sin6_port = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.tcp.port;
- sin6.sin6_flowinfo = inet6->flow_label & IPV6_FLOWINFO_MASK;
+ sin6.sin6_flowinfo = flow_label & IPV6_FLOWINFO_MASK;
memcpy(&sin6.sin6_addr,
&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u3.in6,
sizeof(sin6.sin6_addr));
nf_ct_put(ct);
- sin6.sin6_scope_id = ipv6_iface_scope_id(&sin6.sin6_addr,
- sk->sk_bound_dev_if);
+ sin6.sin6_scope_id = ipv6_iface_scope_id(&sin6.sin6_addr, bound_dev_if);
return copy_to_user(user, &sin6, sizeof(sin6)) ? -EFAULT : 0;
}
diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c
index ce53dcfda88a..b84ce3e6d728 100644
--- a/net/ipv6/netfilter/nf_conntrack_reasm.c
+++ b/net/ipv6/netfilter/nf_conntrack_reasm.c
@@ -264,6 +264,7 @@ static int nf_ct_frag6_queue(struct frag_queue *fq, struct sk_buff *skb,
* this case. -DaveM
*/
pr_debug("end of fragment not rounded to 8 bytes.\n");
+ inet_frag_kill(&fq->q, &nf_frags);
return -EPROTO;
}
if (end > fq->q.len) {
diff --git a/net/ipv6/netfilter/nf_flow_table_ipv6.c b/net/ipv6/netfilter/nf_flow_table_ipv6.c
index fff21602875a..d346705d6ee6 100644
--- a/net/ipv6/netfilter/nf_flow_table_ipv6.c
+++ b/net/ipv6/netfilter/nf_flow_table_ipv6.c
@@ -253,6 +253,7 @@ static struct nf_flowtable_type flowtable_ipv6 = {
.family = NFPROTO_IPV6,
.params = &nf_flow_offload_rhash_params,
.gc = nf_flow_offload_work_gc,
+ .free = nf_flow_table_free,
.hook = nf_flow_offload_ipv6_hook,
.owner = THIS_MODULE,
};
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c
index ddda7eb3c623..4c25339b1984 100644
--- a/net/ipv6/raw.c
+++ b/net/ipv6/raw.c
@@ -1272,6 +1272,8 @@ struct proto rawv6_prot = {
.hash = raw_hash_sk,
.unhash = raw_unhash_sk,
.obj_size = sizeof(struct raw6_sock),
+ .useroffset = offsetof(struct raw6_sock, filter),
+ .usersize = sizeof_field(struct raw6_sock, filter),
.h.raw_hash = &raw_v6_hashinfo,
#ifdef CONFIG_COMPAT
.compat_setsockopt = compat_rawv6_setsockopt,
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index fb2d251c0500..9dcfadddd800 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -2479,7 +2479,7 @@ static int ip6_route_check_nh_onlink(struct net *net,
struct net_device *dev,
struct netlink_ext_ack *extack)
{
- u32 tbid = l3mdev_fib_table(dev) ? : RT_TABLE_LOCAL;
+ u32 tbid = l3mdev_fib_table(dev) ? : RT_TABLE_MAIN;
const struct in6_addr *gw_addr = &cfg->fc_gateway;
u32 flags = RTF_LOCAL | RTF_ANYCAST | RTF_REJECT;
struct rt6_info *grt;
@@ -2488,8 +2488,10 @@ static int ip6_route_check_nh_onlink(struct net *net,
err = 0;
grt = ip6_nh_lookup_table(net, cfg, gw_addr, tbid, 0);
if (grt) {
- if (grt->rt6i_flags & flags || dev != grt->dst.dev) {
- NL_SET_ERR_MSG(extack, "Nexthop has invalid gateway");
+ if (!grt->dst.error &&
+ (grt->rt6i_flags & flags || dev != grt->dst.dev)) {
+ NL_SET_ERR_MSG(extack,
+ "Nexthop has invalid gateway or device mismatch");
err = -EINVAL;
}
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index a1ab29e2ab3b..412139f4eccd 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -942,7 +942,8 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb)
if (sk) {
oif = sk->sk_bound_dev_if;
- trace_tcp_send_reset(sk, skb);
+ if (sk_fullsock(sk))
+ trace_tcp_send_reset(sk, skb);
}
tcp_v6_send_response(sk, skb, seq, ack_seq, 0, 0, 0, oif, key, 1, 0, 0);
diff --git a/net/ipx/Kconfig b/net/ipx/Kconfig
deleted file mode 100644
index e9ad0062fbb6..000000000000
--- a/net/ipx/Kconfig
+++ /dev/null
@@ -1,60 +0,0 @@
-#
-# IPX configuration
-#
-config IPX
- tristate "The IPX protocol"
- select LLC
- ---help---
- This is support for the Novell networking protocol, IPX, commonly
- used for local networks of Windows machines. You need it if you
- want to access Novell NetWare file or print servers using the Linux
- Novell client ncpfs (available from
- <ftp://platan.vc.cvut.cz/pub/linux/ncpfs/>) or from
- within the Linux DOS emulator DOSEMU (read the DOSEMU-HOWTO,
- available from <http://www.tldp.org/docs.html#howto>). In order
- to do the former, you'll also have to say Y to "NCP file system
- support", below.
-
- IPX is similar in scope to IP, while SPX, which runs on top of IPX,
- is similar to TCP.
-
- To turn your Linux box into a fully featured NetWare file server and
- IPX router, say Y here and fetch either lwared from
- <ftp://ibiblio.org/pub/Linux/system/network/daemons/> or
- mars_nwe from <ftp://www.compu-art.de/mars_nwe/>. For more
- information, read the IPX-HOWTO available from
- <http://www.tldp.org/docs.html#howto>.
-
- The IPX driver would enlarge your kernel by about 16 KB. To compile
- this driver as a module, choose M here: the module will be called ipx.
- Unless you want to integrate your Linux box with a local Novell
- network, say N.
-
-config IPX_INTERN
- bool "IPX: Full internal IPX network"
- depends on IPX
- ---help---
- Every IPX network has an address that identifies it. Sometimes it is
- useful to give an IPX "network" address to your Linux box as well
- (for example if your box is acting as a file server for different
- IPX networks: it will then be accessible from everywhere using the
- same address). The way this is done is to create a virtual internal
- "network" inside your box and to assign an IPX address to this
- network. Say Y here if you want to do this; read the IPX-HOWTO at
- <http://www.tldp.org/docs.html#howto> for details.
-
- The full internal IPX network enables you to allocate sockets on
- different virtual nodes of the internal network. This is done by
- evaluating the field sipx_node of the socket address given to the
- bind call. So applications should always initialize the node field
- to 0 when binding a socket on the primary network. In this case the
- socket is assigned the default node that has been given to the
- kernel when the internal network was created. By enabling the full
- internal IPX network the cross-forwarding of packets targeted at
- 'special' sockets to sockets listening on the primary network is
- disabled. This might break existing applications, especially RIP/SAP
- daemons. A RIP/SAP daemon that works well with the full internal net
- can be found on <ftp://ftp.gwdg.de/pub/linux/misc/ncpfs/>.
-
- If you don't know what you are doing, say N.
-
diff --git a/net/ipx/Makefile b/net/ipx/Makefile
deleted file mode 100644
index 440fafa9fd07..000000000000
--- a/net/ipx/Makefile
+++ /dev/null
@@ -1,8 +0,0 @@
-#
-# Makefile for the Linux IPX layer.
-#
-
-obj-$(CONFIG_IPX) += ipx.o
-
-ipx-y := af_ipx.o ipx_route.o ipx_proc.o pe2.o
-ipx-$(CONFIG_SYSCTL) += sysctl_net_ipx.o
diff --git a/net/ipx/af_ipx.c b/net/ipx/af_ipx.c
deleted file mode 100644
index d21a9d128d3e..000000000000
--- a/net/ipx/af_ipx.c
+++ /dev/null
@@ -1,2084 +0,0 @@
-/*
- * Implements an IPX socket layer.
- *
- * This code is derived from work by
- * Ross Biro : Writing the original IP stack
- * Fred Van Kempen : Tidying up the TCP/IP
- *
- * Many thanks go to Keith Baker, Institute For Industrial Information
- * Technology Ltd, Swansea University for allowing me to work on this
- * in my own time even though it was in some ways related to commercial
- * work I am currently employed to do there.
- *
- * All the material in this file is subject to the Gnu license version 2.
- * Neither Alan Cox nor the Swansea University Computer Society admit
- * liability nor provide warranty for any of this software. This material
- * is provided as is and at no charge.
- *
- * Portions Copyright (c) 2000-2003 Conectiva, Inc. <acme@conectiva.com.br>
- * Neither Arnaldo Carvalho de Melo nor Conectiva, Inc. admit liability nor
- * provide warranty for any of this software. This material is provided
- * "AS-IS" and at no charge.
- *
- * Portions Copyright (c) 1995 Caldera, Inc. <greg@caldera.com>
- * Neither Greg Page nor Caldera, Inc. admit liability nor provide
- * warranty for any of this software. This material is provided
- * "AS-IS" and at no charge.
- *
- * See net/ipx/ChangeLog.
- */
-
-#include <linux/capability.h>
-#include <linux/errno.h>
-#include <linux/if_arp.h>
-#include <linux/if_ether.h>
-#include <linux/init.h>
-#include <linux/ipx.h>
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/module.h>
-#include <linux/net.h>
-#include <linux/netdevice.h>
-#include <linux/uio.h>
-#include <linux/slab.h>
-#include <linux/skbuff.h>
-#include <linux/socket.h>
-#include <linux/sockios.h>
-#include <linux/string.h>
-#include <linux/types.h>
-#include <linux/termios.h>
-
-#include <net/ipx.h>
-#include <net/p8022.h>
-#include <net/psnap.h>
-#include <net/sock.h>
-#include <net/datalink.h>
-#include <net/tcp_states.h>
-#include <net/net_namespace.h>
-
-#include <linux/uaccess.h>
-
-/* Configuration Variables */
-static unsigned char ipxcfg_max_hops = 16;
-static char ipxcfg_auto_select_primary;
-static char ipxcfg_auto_create_interfaces;
-int sysctl_ipx_pprop_broadcasting = 1;
-
-/* Global Variables */
-static struct datalink_proto *p8022_datalink;
-static struct datalink_proto *pEII_datalink;
-static struct datalink_proto *p8023_datalink;
-static struct datalink_proto *pSNAP_datalink;
-
-static const struct proto_ops ipx_dgram_ops;
-
-LIST_HEAD(ipx_interfaces);
-DEFINE_SPINLOCK(ipx_interfaces_lock);
-
-struct ipx_interface *ipx_primary_net;
-struct ipx_interface *ipx_internal_net;
-
-struct ipx_interface *ipx_interfaces_head(void)
-{
- struct ipx_interface *rc = NULL;
-
- if (!list_empty(&ipx_interfaces))
- rc = list_entry(ipx_interfaces.next,
- struct ipx_interface, node);
- return rc;
-}
-
-static void ipxcfg_set_auto_select(char val)
-{
- ipxcfg_auto_select_primary = val;
- if (val && !ipx_primary_net)
- ipx_primary_net = ipx_interfaces_head();
-}
-
-static int ipxcfg_get_config_data(struct ipx_config_data __user *arg)
-{
- struct ipx_config_data vals;
-
- vals.ipxcfg_auto_create_interfaces = ipxcfg_auto_create_interfaces;
- vals.ipxcfg_auto_select_primary = ipxcfg_auto_select_primary;
-
- return copy_to_user(arg, &vals, sizeof(vals)) ? -EFAULT : 0;
-}
-
-/*
- * Note: Sockets may not be removed _during_ an interrupt or inet_bh
- * handler using this technique. They can be added although we do not
- * use this facility.
- */
-
-static void ipx_remove_socket(struct sock *sk)
-{
- /* Determine interface with which socket is associated */
- struct ipx_interface *intrfc = ipx_sk(sk)->intrfc;
-
- if (!intrfc)
- goto out;
-
- ipxitf_hold(intrfc);
- spin_lock_bh(&intrfc->if_sklist_lock);
- sk_del_node_init(sk);
- spin_unlock_bh(&intrfc->if_sklist_lock);
- ipxitf_put(intrfc);
-out:
- return;
-}
-
-static void ipx_destroy_socket(struct sock *sk)
-{
- ipx_remove_socket(sk);
- skb_queue_purge(&sk->sk_receive_queue);
- sk_refcnt_debug_dec(sk);
-}
-
-/*
- * The following code is used to support IPX Interfaces (IPXITF). An
- * IPX interface is defined by a physical device and a frame type.
- */
-
-/* ipxitf_clear_primary_net has to be called with ipx_interfaces_lock held */
-
-static void ipxitf_clear_primary_net(void)
-{
- ipx_primary_net = NULL;
- if (ipxcfg_auto_select_primary)
- ipx_primary_net = ipx_interfaces_head();
-}
-
-static struct ipx_interface *__ipxitf_find_using_phys(struct net_device *dev,
- __be16 datalink)
-{
- struct ipx_interface *i;
-
- list_for_each_entry(i, &ipx_interfaces, node)
- if (i->if_dev == dev && i->if_dlink_type == datalink)
- goto out;
- i = NULL;
-out:
- return i;
-}
-
-static struct ipx_interface *ipxitf_find_using_phys(struct net_device *dev,
- __be16 datalink)
-{
- struct ipx_interface *i;
-
- spin_lock_bh(&ipx_interfaces_lock);
- i = __ipxitf_find_using_phys(dev, datalink);
- if (i)
- ipxitf_hold(i);
- spin_unlock_bh(&ipx_interfaces_lock);
- return i;
-}
-
-struct ipx_interface *ipxitf_find_using_net(__be32 net)
-{
- struct ipx_interface *i;
-
- spin_lock_bh(&ipx_interfaces_lock);
- if (net) {
- list_for_each_entry(i, &ipx_interfaces, node)
- if (i->if_netnum == net)
- goto hold;
- i = NULL;
- goto unlock;
- }
-
- i = ipx_primary_net;
- if (i)
-hold:
- ipxitf_hold(i);
-unlock:
- spin_unlock_bh(&ipx_interfaces_lock);
- return i;
-}
-
-/* Sockets are bound to a particular IPX interface. */
-static void ipxitf_insert_socket(struct ipx_interface *intrfc, struct sock *sk)
-{
- ipxitf_hold(intrfc);
- spin_lock_bh(&intrfc->if_sklist_lock);
- ipx_sk(sk)->intrfc = intrfc;
- sk_add_node(sk, &intrfc->if_sklist);
- spin_unlock_bh(&intrfc->if_sklist_lock);
- ipxitf_put(intrfc);
-}
-
-/* caller must hold intrfc->if_sklist_lock */
-static struct sock *__ipxitf_find_socket(struct ipx_interface *intrfc,
- __be16 port)
-{
- struct sock *s;
-
- sk_for_each(s, &intrfc->if_sklist)
- if (ipx_sk(s)->port == port)
- goto found;
- s = NULL;
-found:
- return s;
-}
-
-/* caller must hold a reference to intrfc */
-static struct sock *ipxitf_find_socket(struct ipx_interface *intrfc,
- __be16 port)
-{
- struct sock *s;
-
- spin_lock_bh(&intrfc->if_sklist_lock);
- s = __ipxitf_find_socket(intrfc, port);
- if (s)
- sock_hold(s);
- spin_unlock_bh(&intrfc->if_sklist_lock);
-
- return s;
-}
-
-#ifdef CONFIG_IPX_INTERN
-static struct sock *ipxitf_find_internal_socket(struct ipx_interface *intrfc,
- unsigned char *ipx_node,
- __be16 port)
-{
- struct sock *s;
-
- ipxitf_hold(intrfc);
- spin_lock_bh(&intrfc->if_sklist_lock);
-
- sk_for_each(s, &intrfc->if_sklist) {
- struct ipx_sock *ipxs = ipx_sk(s);
-
- if (ipxs->port == port &&
- !memcmp(ipx_node, ipxs->node, IPX_NODE_LEN))
- goto found;
- }
- s = NULL;
-found:
- spin_unlock_bh(&intrfc->if_sklist_lock);
- ipxitf_put(intrfc);
- return s;
-}
-#endif
-
-static void __ipxitf_down(struct ipx_interface *intrfc)
-{
- struct sock *s;
- struct hlist_node *t;
-
- /* Delete all routes associated with this interface */
- ipxrtr_del_routes(intrfc);
-
- spin_lock_bh(&intrfc->if_sklist_lock);
- /* error sockets */
- sk_for_each_safe(s, t, &intrfc->if_sklist) {
- struct ipx_sock *ipxs = ipx_sk(s);
-
- s->sk_err = ENOLINK;
- s->sk_error_report(s);
- ipxs->intrfc = NULL;
- ipxs->port = 0;
- sock_set_flag(s, SOCK_ZAPPED); /* Indicates it is no longer bound */
- sk_del_node_init(s);
- }
- INIT_HLIST_HEAD(&intrfc->if_sklist);
- spin_unlock_bh(&intrfc->if_sklist_lock);
-
- /* remove this interface from list */
- list_del(&intrfc->node);
-
- /* remove this interface from *special* networks */
- if (intrfc == ipx_primary_net)
- ipxitf_clear_primary_net();
- if (intrfc == ipx_internal_net)
- ipx_internal_net = NULL;
-
- if (intrfc->if_dev)
- dev_put(intrfc->if_dev);
- kfree(intrfc);
-}
-
-void ipxitf_down(struct ipx_interface *intrfc)
-{
- spin_lock_bh(&ipx_interfaces_lock);
- __ipxitf_down(intrfc);
- spin_unlock_bh(&ipx_interfaces_lock);
-}
-
-static void __ipxitf_put(struct ipx_interface *intrfc)
-{
- if (refcount_dec_and_test(&intrfc->refcnt))
- __ipxitf_down(intrfc);
-}
-
-static int ipxitf_device_event(struct notifier_block *notifier,
- unsigned long event, void *ptr)
-{
- struct net_device *dev = netdev_notifier_info_to_dev(ptr);
- struct ipx_interface *i, *tmp;
-
- if (!net_eq(dev_net(dev), &init_net))
- return NOTIFY_DONE;
-
- if (event != NETDEV_DOWN && event != NETDEV_UP)
- goto out;
-
- spin_lock_bh(&ipx_interfaces_lock);
- list_for_each_entry_safe(i, tmp, &ipx_interfaces, node)
- if (i->if_dev == dev) {
- if (event == NETDEV_UP)
- ipxitf_hold(i);
- else
- __ipxitf_put(i);
- }
- spin_unlock_bh(&ipx_interfaces_lock);
-out:
- return NOTIFY_DONE;
-}
-
-
-static __exit void ipxitf_cleanup(void)
-{
- struct ipx_interface *i, *tmp;
-
- spin_lock_bh(&ipx_interfaces_lock);
- list_for_each_entry_safe(i, tmp, &ipx_interfaces, node)
- __ipxitf_put(i);
- spin_unlock_bh(&ipx_interfaces_lock);
-}
-
-static void ipxitf_def_skb_handler(struct sock *sock, struct sk_buff *skb)
-{
- if (sock_queue_rcv_skb(sock, skb) < 0)
- kfree_skb(skb);
-}
-
-/*
- * On input skb->sk is NULL. Nobody is charged for the memory.
- */
-
-/* caller must hold a reference to intrfc */
-
-#ifdef CONFIG_IPX_INTERN
-static int ipxitf_demux_socket(struct ipx_interface *intrfc,
- struct sk_buff *skb, int copy)
-{
- struct ipxhdr *ipx = ipx_hdr(skb);
- int is_broadcast = !memcmp(ipx->ipx_dest.node, ipx_broadcast_node,
- IPX_NODE_LEN);
- struct sock *s;
- int rc;
-
- spin_lock_bh(&intrfc->if_sklist_lock);
-
- sk_for_each(s, &intrfc->if_sklist) {
- struct ipx_sock *ipxs = ipx_sk(s);
-
- if (ipxs->port == ipx->ipx_dest.sock &&
- (is_broadcast || !memcmp(ipx->ipx_dest.node,
- ipxs->node, IPX_NODE_LEN))) {
- /* We found a socket to which to send */
- struct sk_buff *skb1;
-
- if (copy) {
- skb1 = skb_clone(skb, GFP_ATOMIC);
- rc = -ENOMEM;
- if (!skb1)
- goto out;
- } else {
- skb1 = skb;
- copy = 1; /* skb may only be used once */
- }
- ipxitf_def_skb_handler(s, skb1);
-
- /* On an external interface, one socket can listen */
- if (intrfc != ipx_internal_net)
- break;
- }
- }
-
- /* skb was solely for us, and we did not make a copy, so free it. */
- if (!copy)
- kfree_skb(skb);
-
- rc = 0;
-out:
- spin_unlock_bh(&intrfc->if_sklist_lock);
- return rc;
-}
-#else
-static struct sock *ncp_connection_hack(struct ipx_interface *intrfc,
- struct ipxhdr *ipx)
-{
- /* The packet's target is a NCP connection handler. We want to hand it
- * to the correct socket directly within the kernel, so that the
- * mars_nwe packet distribution process does not have to do it. Here we
- * only care about NCP and BURST packets.
- *
- * You might call this a hack, but believe me, you do not want a
- * complete NCP layer in the kernel, and this is VERY fast as well. */
- struct sock *sk = NULL;
- int connection = 0;
- u8 *ncphdr = (u8 *)(ipx + 1);
-
- if (*ncphdr == 0x22 && *(ncphdr + 1) == 0x22) /* NCP request */
- connection = (((int) *(ncphdr + 5)) << 8) | (int) *(ncphdr + 3);
- else if (*ncphdr == 0x77 && *(ncphdr + 1) == 0x77) /* BURST packet */
- connection = (((int) *(ncphdr + 9)) << 8) | (int) *(ncphdr + 8);
-
- if (connection) {
- /* Now we have to look for a special NCP connection handling
- * socket. Only these sockets have ipx_ncp_conn != 0, set by
- * SIOCIPXNCPCONN. */
- spin_lock_bh(&intrfc->if_sklist_lock);
- sk_for_each(sk, &intrfc->if_sklist)
- if (ipx_sk(sk)->ipx_ncp_conn == connection) {
- sock_hold(sk);
- goto found;
- }
- sk = NULL;
- found:
- spin_unlock_bh(&intrfc->if_sklist_lock);
- }
- return sk;
-}
-
-static int ipxitf_demux_socket(struct ipx_interface *intrfc,
- struct sk_buff *skb, int copy)
-{
- struct ipxhdr *ipx = ipx_hdr(skb);
- struct sock *sock1 = NULL, *sock2 = NULL;
- struct sk_buff *skb1 = NULL, *skb2 = NULL;
- int rc;
-
- if (intrfc == ipx_primary_net && ntohs(ipx->ipx_dest.sock) == 0x451)
- sock1 = ncp_connection_hack(intrfc, ipx);
- if (!sock1)
- /* No special socket found, forward the packet the normal way */
- sock1 = ipxitf_find_socket(intrfc, ipx->ipx_dest.sock);
-
- /*
- * We need to check if there is a primary net and if
- * this is addressed to one of the *SPECIAL* sockets because
- * these need to be propagated to the primary net.
- * The *SPECIAL* socket list contains: 0x452(SAP), 0x453(RIP) and
- * 0x456(Diagnostic).
- */
-
- if (ipx_primary_net && intrfc != ipx_primary_net) {
- const int dsock = ntohs(ipx->ipx_dest.sock);
-
- if (dsock == 0x452 || dsock == 0x453 || dsock == 0x456)
- /* The appropriate thing to do here is to dup the
- * packet and route to the primary net interface via
- * ipxitf_send; however, we'll cheat and just demux it
- * here. */
- sock2 = ipxitf_find_socket(ipx_primary_net,
- ipx->ipx_dest.sock);
- }
-
- /*
- * If there is nothing to do return. The kfree will cancel any charging.
- */
- rc = 0;
- if (!sock1 && !sock2) {
- if (!copy)
- kfree_skb(skb);
- goto out;
- }
-
- /*
- * This next segment of code is a little awkward, but it sets it up
- * so that the appropriate number of copies of the SKB are made and
- * that skb1 and skb2 point to it (them) so that it (they) can be
- * demuxed to sock1 and/or sock2. If we are unable to make enough
- * copies, we do as much as is possible.
- */
-
- if (copy)
- skb1 = skb_clone(skb, GFP_ATOMIC);
- else
- skb1 = skb;
-
- rc = -ENOMEM;
- if (!skb1)
- goto out_put;
-
- /* Do we need 2 SKBs? */
- if (sock1 && sock2)
- skb2 = skb_clone(skb1, GFP_ATOMIC);
- else
- skb2 = skb1;
-
- if (sock1)
- ipxitf_def_skb_handler(sock1, skb1);
-
- if (!skb2)
- goto out_put;
-
- if (sock2)
- ipxitf_def_skb_handler(sock2, skb2);
-
- rc = 0;
-out_put:
- if (sock1)
- sock_put(sock1);
- if (sock2)
- sock_put(sock2);
-out:
- return rc;
-}
-#endif /* CONFIG_IPX_INTERN */
-
-static struct sk_buff *ipxitf_adjust_skbuff(struct ipx_interface *intrfc,
- struct sk_buff *skb)
-{
- struct sk_buff *skb2;
- int in_offset = (unsigned char *)ipx_hdr(skb) - skb->head;
- int out_offset = intrfc->if_ipx_offset;
- int len;
-
- /* Hopefully, most cases */
- if (in_offset >= out_offset)
- return skb;
-
- /* Need new SKB */
- len = skb->len + out_offset;
- skb2 = alloc_skb(len, GFP_ATOMIC);
- if (skb2) {
- skb_reserve(skb2, out_offset);
- skb_reset_network_header(skb2);
- skb_reset_transport_header(skb2);
- skb_put(skb2, skb->len);
- memcpy(ipx_hdr(skb2), ipx_hdr(skb), skb->len);
- memcpy(skb2->cb, skb->cb, sizeof(skb->cb));
- }
- kfree_skb(skb);
- return skb2;
-}
-
-/* caller must hold a reference to intrfc and the skb has to be unshared */
-int ipxitf_send(struct ipx_interface *intrfc, struct sk_buff *skb, char *node)
-{
- struct ipxhdr *ipx = ipx_hdr(skb);
- struct net_device *dev = intrfc->if_dev;
- struct datalink_proto *dl = intrfc->if_dlink;
- char dest_node[IPX_NODE_LEN];
- int send_to_wire = 1;
- int addr_len;
-
- ipx->ipx_tctrl = IPX_SKB_CB(skb)->ipx_tctrl;
- ipx->ipx_dest.net = IPX_SKB_CB(skb)->ipx_dest_net;
- ipx->ipx_source.net = IPX_SKB_CB(skb)->ipx_source_net;
-
- /* see if we need to include the netnum in the route list */
- if (IPX_SKB_CB(skb)->last_hop.index >= 0) {
- __be32 *last_hop = (__be32 *)(((u8 *) skb->data) +
- sizeof(struct ipxhdr) +
- IPX_SKB_CB(skb)->last_hop.index *
- sizeof(__be32));
- *last_hop = IPX_SKB_CB(skb)->last_hop.netnum;
- IPX_SKB_CB(skb)->last_hop.index = -1;
- }
-
- /*
- * We need to know how many skbuffs it will take to send out this
- * packet to avoid unnecessary copies.
- */
-
- if (!dl || !dev || dev->flags & IFF_LOOPBACK)
- send_to_wire = 0; /* No non looped */
-
- /*
- * See if this should be demuxed to sockets on this interface
- *
- * We want to ensure the original was eaten or that we only use
- * up clones.
- */
-
- if (ipx->ipx_dest.net == intrfc->if_netnum) {
- /*
- * To our own node, loop and free the original.
- * The internal net will receive on all node address.
- */
- if (intrfc == ipx_internal_net ||
- !memcmp(intrfc->if_node, node, IPX_NODE_LEN)) {
- /* Don't charge sender */
- skb_orphan(skb);
-
- /* Will charge receiver */
- return ipxitf_demux_socket(intrfc, skb, 0);
- }
-
- /* Broadcast, loop and possibly keep to send on. */
- if (!memcmp(ipx_broadcast_node, node, IPX_NODE_LEN)) {
- if (!send_to_wire)
- skb_orphan(skb);
- ipxitf_demux_socket(intrfc, skb, send_to_wire);
- if (!send_to_wire)
- goto out;
- }
- }
-
- /*
- * If the originating net is not equal to our net; this is routed
- * We are still charging the sender. Which is right - the driver
- * free will handle this fairly.
- */
- if (ipx->ipx_source.net != intrfc->if_netnum) {
- /*
- * Unshare the buffer before modifying the count in
- * case it's a flood or tcpdump
- */
- skb = skb_unshare(skb, GFP_ATOMIC);
- if (!skb)
- goto out;
- if (++ipx->ipx_tctrl > ipxcfg_max_hops)
- send_to_wire = 0;
- }
-
- if (!send_to_wire) {
- kfree_skb(skb);
- goto out;
- }
-
- /* Determine the appropriate hardware address */
- addr_len = dev->addr_len;
- if (!memcmp(ipx_broadcast_node, node, IPX_NODE_LEN))
- memcpy(dest_node, dev->broadcast, addr_len);
- else
- memcpy(dest_node, &(node[IPX_NODE_LEN-addr_len]), addr_len);
-
- /* Make any compensation for differing physical/data link size */
- skb = ipxitf_adjust_skbuff(intrfc, skb);
- if (!skb)
- goto out;
-
- /* set up data link and physical headers */
- skb->dev = dev;
- skb->protocol = htons(ETH_P_IPX);
-
- /* Send it out */
- dl->request(dl, skb, dest_node);
-out:
- return 0;
-}
-
-static int ipxitf_add_local_route(struct ipx_interface *intrfc)
-{
- return ipxrtr_add_route(intrfc->if_netnum, intrfc, NULL);
-}
-
-static void ipxitf_discover_netnum(struct ipx_interface *intrfc,
- struct sk_buff *skb);
-static int ipxitf_pprop(struct ipx_interface *intrfc, struct sk_buff *skb);
-
-static int ipxitf_rcv(struct ipx_interface *intrfc, struct sk_buff *skb)
-{
- struct ipxhdr *ipx = ipx_hdr(skb);
- int rc = 0;
-
- ipxitf_hold(intrfc);
-
- /* See if we should update our network number */
- if (!intrfc->if_netnum) /* net number of intrfc not known yet */
- ipxitf_discover_netnum(intrfc, skb);
-
- IPX_SKB_CB(skb)->last_hop.index = -1;
- if (ipx->ipx_type == IPX_TYPE_PPROP) {
- rc = ipxitf_pprop(intrfc, skb);
- if (rc)
- goto out_free_skb;
- }
-
- /* local processing follows */
- if (!IPX_SKB_CB(skb)->ipx_dest_net)
- IPX_SKB_CB(skb)->ipx_dest_net = intrfc->if_netnum;
- if (!IPX_SKB_CB(skb)->ipx_source_net)
- IPX_SKB_CB(skb)->ipx_source_net = intrfc->if_netnum;
-
- /* it doesn't make sense to route a pprop packet, there's no meaning
- * in the ipx_dest_net for such packets */
- if (ipx->ipx_type != IPX_TYPE_PPROP &&
- intrfc->if_netnum != IPX_SKB_CB(skb)->ipx_dest_net) {
- /* We only route point-to-point packets. */
- if (skb->pkt_type == PACKET_HOST) {
- skb = skb_unshare(skb, GFP_ATOMIC);
- if (skb)
- rc = ipxrtr_route_skb(skb);
- goto out_intrfc;
- }
-
- goto out_free_skb;
- }
-
- /* see if we should keep it */
- if (!memcmp(ipx_broadcast_node, ipx->ipx_dest.node, IPX_NODE_LEN) ||
- !memcmp(intrfc->if_node, ipx->ipx_dest.node, IPX_NODE_LEN)) {
- rc = ipxitf_demux_socket(intrfc, skb, 0);
- goto out_intrfc;
- }
-
- /* we couldn't pawn it off so unload it */
-out_free_skb:
- kfree_skb(skb);
-out_intrfc:
- ipxitf_put(intrfc);
- return rc;
-}
-
-static void ipxitf_discover_netnum(struct ipx_interface *intrfc,
- struct sk_buff *skb)
-{
- const struct ipx_cb *cb = IPX_SKB_CB(skb);
-
- /* see if this is an intra packet: source_net == dest_net */
- if (cb->ipx_source_net == cb->ipx_dest_net && cb->ipx_source_net) {
- struct ipx_interface *i =
- ipxitf_find_using_net(cb->ipx_source_net);
- /* NB: NetWare servers lie about their hop count so we
- * dropped the test based on it. This is the best way
- * to determine this is a 0 hop count packet. */
- if (!i) {
- intrfc->if_netnum = cb->ipx_source_net;
- ipxitf_add_local_route(intrfc);
- } else {
- printk(KERN_WARNING "IPX: Network number collision "
- "%lx\n %s %s and %s %s\n",
- (unsigned long) ntohl(cb->ipx_source_net),
- ipx_device_name(i),
- ipx_frame_name(i->if_dlink_type),
- ipx_device_name(intrfc),
- ipx_frame_name(intrfc->if_dlink_type));
- ipxitf_put(i);
- }
- }
-}
-
-/**
- * ipxitf_pprop - Process packet propagation IPX packet type 0x14, used for
- * NetBIOS broadcasts
- * @intrfc: IPX interface receiving this packet
- * @skb: Received packet
- *
- * Checks if packet is valid: if its more than %IPX_MAX_PPROP_HOPS hops or if it
- * is smaller than a IPX header + the room for %IPX_MAX_PPROP_HOPS hops we drop
- * it, not even processing it locally, if it has exact %IPX_MAX_PPROP_HOPS we
- * don't broadcast it, but process it locally. See chapter 5 of Novell's "IPX
- * RIP and SAP Router Specification", Part Number 107-000029-001.
- *
- * If it is valid, check if we have pprop broadcasting enabled by the user,
- * if not, just return zero for local processing.
- *
- * If it is enabled check the packet and don't broadcast it if we have already
- * seen this packet.
- *
- * Broadcast: send it to the interfaces that aren't on the packet visited nets
- * array, just after the IPX header.
- *
- * Returns -EINVAL for invalid packets, so that the calling function drops
- * the packet without local processing. 0 if packet is to be locally processed.
- */
-static int ipxitf_pprop(struct ipx_interface *intrfc, struct sk_buff *skb)
-{
- struct ipxhdr *ipx = ipx_hdr(skb);
- int i, rc = -EINVAL;
- struct ipx_interface *ifcs;
- char *c;
- __be32 *l;
-
- /* Illegal packet - too many hops or too short */
- /* We decide to throw it away: no broadcasting, no local processing.
- * NetBIOS unaware implementations route them as normal packets -
- * tctrl <= 15, any data payload... */
- if (IPX_SKB_CB(skb)->ipx_tctrl > IPX_MAX_PPROP_HOPS ||
- ntohs(ipx->ipx_pktsize) < sizeof(struct ipxhdr) +
- IPX_MAX_PPROP_HOPS * sizeof(u32))
- goto out;
- /* are we broadcasting this damn thing? */
- rc = 0;
- if (!sysctl_ipx_pprop_broadcasting)
- goto out;
- /* We do broadcast packet on the IPX_MAX_PPROP_HOPS hop, but we
- * process it locally. All previous hops broadcasted it, and process it
- * locally. */
- if (IPX_SKB_CB(skb)->ipx_tctrl == IPX_MAX_PPROP_HOPS)
- goto out;
-
- c = ((u8 *) ipx) + sizeof(struct ipxhdr);
- l = (__be32 *) c;
-
- /* Don't broadcast packet if already seen this net */
- for (i = 0; i < IPX_SKB_CB(skb)->ipx_tctrl; i++)
- if (*l++ == intrfc->if_netnum)
- goto out;
-
- /* < IPX_MAX_PPROP_HOPS hops && input interface not in list. Save the
- * position where we will insert recvd netnum into list, later on,
- * in ipxitf_send */
- IPX_SKB_CB(skb)->last_hop.index = i;
- IPX_SKB_CB(skb)->last_hop.netnum = intrfc->if_netnum;
- /* xmit on all other interfaces... */
- spin_lock_bh(&ipx_interfaces_lock);
- list_for_each_entry(ifcs, &ipx_interfaces, node) {
- /* Except unconfigured interfaces */
- if (!ifcs->if_netnum)
- continue;
-
- /* That aren't in the list */
- if (ifcs == intrfc)
- continue;
- l = (__be32 *) c;
- /* don't consider the last entry in the packet list,
- * it is our netnum, and it is not there yet */
- for (i = 0; i < IPX_SKB_CB(skb)->ipx_tctrl; i++)
- if (ifcs->if_netnum == *l++)
- break;
- if (i == IPX_SKB_CB(skb)->ipx_tctrl) {
- struct sk_buff *s = skb_copy(skb, GFP_ATOMIC);
-
- if (s) {
- IPX_SKB_CB(s)->ipx_dest_net = ifcs->if_netnum;
- ipxrtr_route_skb(s);
- }
- }
- }
- spin_unlock_bh(&ipx_interfaces_lock);
-out:
- return rc;
-}
-
-static void ipxitf_insert(struct ipx_interface *intrfc)
-{
- spin_lock_bh(&ipx_interfaces_lock);
- list_add_tail(&intrfc->node, &ipx_interfaces);
- spin_unlock_bh(&ipx_interfaces_lock);
-
- if (ipxcfg_auto_select_primary && !ipx_primary_net)
- ipx_primary_net = intrfc;
-}
-
-static struct ipx_interface *ipxitf_alloc(struct net_device *dev, __be32 netnum,
- __be16 dlink_type,
- struct datalink_proto *dlink,
- unsigned char internal,
- int ipx_offset)
-{
- struct ipx_interface *intrfc = kmalloc(sizeof(*intrfc), GFP_ATOMIC);
-
- if (intrfc) {
- intrfc->if_dev = dev;
- intrfc->if_netnum = netnum;
- intrfc->if_dlink_type = dlink_type;
- intrfc->if_dlink = dlink;
- intrfc->if_internal = internal;
- intrfc->if_ipx_offset = ipx_offset;
- intrfc->if_sknum = IPX_MIN_EPHEMERAL_SOCKET;
- INIT_HLIST_HEAD(&intrfc->if_sklist);
- refcount_set(&intrfc->refcnt, 1);
- spin_lock_init(&intrfc->if_sklist_lock);
- }
-
- return intrfc;
-}
-
-static int ipxitf_create_internal(struct ipx_interface_definition *idef)
-{
- struct ipx_interface *intrfc;
- int rc = -EEXIST;
-
- /* Only one primary network allowed */
- if (ipx_primary_net)
- goto out;
-
- /* Must have a valid network number */
- rc = -EADDRNOTAVAIL;
- if (!idef->ipx_network)
- goto out;
- intrfc = ipxitf_find_using_net(idef->ipx_network);
- rc = -EADDRINUSE;
- if (intrfc) {
- ipxitf_put(intrfc);
- goto out;
- }
- intrfc = ipxitf_alloc(NULL, idef->ipx_network, 0, NULL, 1, 0);
- rc = -EAGAIN;
- if (!intrfc)
- goto out;
- memcpy((char *)&(intrfc->if_node), idef->ipx_node, IPX_NODE_LEN);
- ipx_internal_net = ipx_primary_net = intrfc;
- ipxitf_hold(intrfc);
- ipxitf_insert(intrfc);
-
- rc = ipxitf_add_local_route(intrfc);
- ipxitf_put(intrfc);
-out:
- return rc;
-}
-
-static __be16 ipx_map_frame_type(unsigned char type)
-{
- __be16 rc = 0;
-
- switch (type) {
- case IPX_FRAME_ETHERII: rc = htons(ETH_P_IPX); break;
- case IPX_FRAME_8022: rc = htons(ETH_P_802_2); break;
- case IPX_FRAME_SNAP: rc = htons(ETH_P_SNAP); break;
- case IPX_FRAME_8023: rc = htons(ETH_P_802_3); break;
- }
-
- return rc;
-}
-
-static int ipxitf_create(struct ipx_interface_definition *idef)
-{
- struct net_device *dev;
- __be16 dlink_type = 0;
- struct datalink_proto *datalink = NULL;
- struct ipx_interface *intrfc;
- int rc;
-
- if (idef->ipx_special == IPX_INTERNAL) {
- rc = ipxitf_create_internal(idef);
- goto out;
- }
-
- rc = -EEXIST;
- if (idef->ipx_special == IPX_PRIMARY && ipx_primary_net)
- goto out;
-
- intrfc = ipxitf_find_using_net(idef->ipx_network);
- rc = -EADDRINUSE;
- if (idef->ipx_network && intrfc) {
- ipxitf_put(intrfc);
- goto out;
- }
-
- if (intrfc)
- ipxitf_put(intrfc);
-
- dev = dev_get_by_name(&init_net, idef->ipx_device);
- rc = -ENODEV;
- if (!dev)
- goto out;
-
- switch (idef->ipx_dlink_type) {
- case IPX_FRAME_8022:
- dlink_type = htons(ETH_P_802_2);
- datalink = p8022_datalink;
- break;
- case IPX_FRAME_ETHERII:
- if (dev->type != ARPHRD_IEEE802) {
- dlink_type = htons(ETH_P_IPX);
- datalink = pEII_datalink;
- break;
- }
- /* fall through */
- case IPX_FRAME_SNAP:
- dlink_type = htons(ETH_P_SNAP);
- datalink = pSNAP_datalink;
- break;
- case IPX_FRAME_8023:
- dlink_type = htons(ETH_P_802_3);
- datalink = p8023_datalink;
- break;
- case IPX_FRAME_NONE:
- default:
- rc = -EPROTONOSUPPORT;
- goto out_dev;
- }
-
- rc = -ENETDOWN;
- if (!(dev->flags & IFF_UP))
- goto out_dev;
-
- /* Check addresses are suitable */
- rc = -EINVAL;
- if (dev->addr_len > IPX_NODE_LEN)
- goto out_dev;
-
- intrfc = ipxitf_find_using_phys(dev, dlink_type);
- if (!intrfc) {
- /* Ok now create */
- intrfc = ipxitf_alloc(dev, idef->ipx_network, dlink_type,
- datalink, 0, dev->hard_header_len +
- datalink->header_length);
- rc = -EAGAIN;
- if (!intrfc)
- goto out_dev;
- /* Setup primary if necessary */
- if (idef->ipx_special == IPX_PRIMARY)
- ipx_primary_net = intrfc;
- if (!memcmp(idef->ipx_node, "\000\000\000\000\000\000",
- IPX_NODE_LEN)) {
- memset(intrfc->if_node, 0, IPX_NODE_LEN);
- memcpy(intrfc->if_node + IPX_NODE_LEN - dev->addr_len,
- dev->dev_addr, dev->addr_len);
- } else
- memcpy(intrfc->if_node, idef->ipx_node, IPX_NODE_LEN);
- ipxitf_hold(intrfc);
- ipxitf_insert(intrfc);
- }
-
-
- /* If the network number is known, add a route */
- rc = 0;
- if (!intrfc->if_netnum)
- goto out_intrfc;
-
- rc = ipxitf_add_local_route(intrfc);
-out_intrfc:
- ipxitf_put(intrfc);
- goto out;
-out_dev:
- dev_put(dev);
-out:
- return rc;
-}
-
-static int ipxitf_delete(struct ipx_interface_definition *idef)
-{
- struct net_device *dev = NULL;
- __be16 dlink_type = 0;
- struct ipx_interface *intrfc;
- int rc = 0;
-
- spin_lock_bh(&ipx_interfaces_lock);
- if (idef->ipx_special == IPX_INTERNAL) {
- if (ipx_internal_net) {
- __ipxitf_put(ipx_internal_net);
- goto out;
- }
- rc = -ENOENT;
- goto out;
- }
-
- dlink_type = ipx_map_frame_type(idef->ipx_dlink_type);
- rc = -EPROTONOSUPPORT;
- if (!dlink_type)
- goto out;
-
- dev = __dev_get_by_name(&init_net, idef->ipx_device);
- rc = -ENODEV;
- if (!dev)
- goto out;
-
- intrfc = __ipxitf_find_using_phys(dev, dlink_type);
- rc = -EINVAL;
- if (!intrfc)
- goto out;
- __ipxitf_put(intrfc);
-
- rc = 0;
-out:
- spin_unlock_bh(&ipx_interfaces_lock);
- return rc;
-}
-
-static struct ipx_interface *ipxitf_auto_create(struct net_device *dev,
- __be16 dlink_type)
-{
- struct ipx_interface *intrfc = NULL;
- struct datalink_proto *datalink;
-
- if (!dev)
- goto out;
-
- /* Check addresses are suitable */
- if (dev->addr_len > IPX_NODE_LEN)
- goto out;
-
- switch (ntohs(dlink_type)) {
- case ETH_P_IPX: datalink = pEII_datalink; break;
- case ETH_P_802_2: datalink = p8022_datalink; break;
- case ETH_P_SNAP: datalink = pSNAP_datalink; break;
- case ETH_P_802_3: datalink = p8023_datalink; break;
- default: goto out;
- }
-
- intrfc = ipxitf_alloc(dev, 0, dlink_type, datalink, 0,
- dev->hard_header_len + datalink->header_length);
-
- if (intrfc) {
- memset(intrfc->if_node, 0, IPX_NODE_LEN);
- memcpy((char *)&(intrfc->if_node[IPX_NODE_LEN-dev->addr_len]),
- dev->dev_addr, dev->addr_len);
- spin_lock_init(&intrfc->if_sklist_lock);
- refcount_set(&intrfc->refcnt, 1);
- ipxitf_insert(intrfc);
- dev_hold(dev);
- }
-
-out:
- return intrfc;
-}
-
-static int ipxitf_ioctl(unsigned int cmd, void __user *arg)
-{
- int rc = -EINVAL;
- struct ifreq ifr;
- int val;
-
- switch (cmd) {
- case SIOCSIFADDR: {
- struct sockaddr_ipx *sipx;
- struct ipx_interface_definition f;
-
- rc = -EFAULT;
- if (copy_from_user(&ifr, arg, sizeof(ifr)))
- break;
- sipx = (struct sockaddr_ipx *)&ifr.ifr_addr;
- rc = -EINVAL;
- if (sipx->sipx_family != AF_IPX)
- break;
- f.ipx_network = sipx->sipx_network;
- memcpy(f.ipx_device, ifr.ifr_name,
- sizeof(f.ipx_device));
- memcpy(f.ipx_node, sipx->sipx_node, IPX_NODE_LEN);
- f.ipx_dlink_type = sipx->sipx_type;
- f.ipx_special = sipx->sipx_special;
-
- if (sipx->sipx_action == IPX_DLTITF)
- rc = ipxitf_delete(&f);
- else
- rc = ipxitf_create(&f);
- break;
- }
- case SIOCGIFADDR: {
- struct sockaddr_ipx *sipx;
- struct ipx_interface *ipxif;
- struct net_device *dev;
-
- rc = -EFAULT;
- if (copy_from_user(&ifr, arg, sizeof(ifr)))
- break;
- sipx = (struct sockaddr_ipx *)&ifr.ifr_addr;
- dev = __dev_get_by_name(&init_net, ifr.ifr_name);
- rc = -ENODEV;
- if (!dev)
- break;
- ipxif = ipxitf_find_using_phys(dev,
- ipx_map_frame_type(sipx->sipx_type));
- rc = -EADDRNOTAVAIL;
- if (!ipxif)
- break;
-
- sipx->sipx_family = AF_IPX;
- sipx->sipx_network = ipxif->if_netnum;
- memcpy(sipx->sipx_node, ipxif->if_node,
- sizeof(sipx->sipx_node));
- rc = 0;
- if (copy_to_user(arg, &ifr, sizeof(ifr)))
- rc = -EFAULT;
- ipxitf_put(ipxif);
- break;
- }
- case SIOCAIPXITFCRT:
- rc = -EFAULT;
- if (get_user(val, (unsigned char __user *) arg))
- break;
- rc = 0;
- ipxcfg_auto_create_interfaces = val;
- break;
- case SIOCAIPXPRISLT:
- rc = -EFAULT;
- if (get_user(val, (unsigned char __user *) arg))
- break;
- rc = 0;
- ipxcfg_set_auto_select(val);
- break;
- }
-
- return rc;
-}
-
-/*
- * Checksum routine for IPX
- */
-
-/* Note: We assume ipx_tctrl==0 and htons(length)==ipx_pktsize */
-/* This functions should *not* mess with packet contents */
-
-__be16 ipx_cksum(struct ipxhdr *packet, int length)
-{
- /*
- * NOTE: sum is a net byte order quantity, which optimizes the
- * loop. This only works on big and little endian machines. (I
- * don't know of a machine that isn't.)
- */
- /* handle the first 3 words separately; checksum should be skipped
- * and ipx_tctrl masked out */
- __u16 *p = (__u16 *)packet;
- __u32 sum = p[1] + (p[2] & (__force u16)htons(0x00ff));
- __u32 i = (length >> 1) - 3; /* Number of remaining complete words */
-
- /* Loop through them */
- p += 3;
- while (i--)
- sum += *p++;
-
- /* Add on the last part word if it exists */
- if (packet->ipx_pktsize & htons(1))
- sum += (__force u16)htons(0xff00) & *p;
-
- /* Do final fixup */
- sum = (sum & 0xffff) + (sum >> 16);
-
- /* It's a pity there's no concept of carry in C */
- if (sum >= 0x10000)
- sum++;
-
- /*
- * Leave 0 alone; we don't want 0xffff here. Note that we can't get
- * here with 0x10000, so this check is the same as ((__u16)sum)
- */
- if (sum)
- sum = ~sum;
-
- return (__force __be16)sum;
-}
-
-const char *ipx_frame_name(__be16 frame)
-{
- char* rc = "None";
-
- switch (ntohs(frame)) {
- case ETH_P_IPX: rc = "EtherII"; break;
- case ETH_P_802_2: rc = "802.2"; break;
- case ETH_P_SNAP: rc = "SNAP"; break;
- case ETH_P_802_3: rc = "802.3"; break;
- }
-
- return rc;
-}
-
-const char *ipx_device_name(struct ipx_interface *intrfc)
-{
- return intrfc->if_internal ? "Internal" :
- intrfc->if_dev ? intrfc->if_dev->name : "Unknown";
-}
-
-/* Handling for system calls applied via the various interfaces to an IPX
- * socket object. */
-
-static int ipx_setsockopt(struct socket *sock, int level, int optname,
- char __user *optval, unsigned int optlen)
-{
- struct sock *sk = sock->sk;
- int opt;
- int rc = -EINVAL;
-
- lock_sock(sk);
- if (optlen != sizeof(int))
- goto out;
-
- rc = -EFAULT;
- if (get_user(opt, (unsigned int __user *)optval))
- goto out;
-
- rc = -ENOPROTOOPT;
- if (!(level == SOL_IPX && optname == IPX_TYPE))
- goto out;
-
- ipx_sk(sk)->type = opt;
- rc = 0;
-out:
- release_sock(sk);
- return rc;
-}
-
-static int ipx_getsockopt(struct socket *sock, int level, int optname,
- char __user *optval, int __user *optlen)
-{
- struct sock *sk = sock->sk;
- int val = 0;
- int len;
- int rc = -ENOPROTOOPT;
-
- lock_sock(sk);
- if (!(level == SOL_IPX && optname == IPX_TYPE))
- goto out;
-
- val = ipx_sk(sk)->type;
-
- rc = -EFAULT;
- if (get_user(len, optlen))
- goto out;
-
- len = min_t(unsigned int, len, sizeof(int));
- rc = -EINVAL;
- if(len < 0)
- goto out;
-
- rc = -EFAULT;
- if (put_user(len, optlen) || copy_to_user(optval, &val, len))
- goto out;
-
- rc = 0;
-out:
- release_sock(sk);
- return rc;
-}
-
-static struct proto ipx_proto = {
- .name = "IPX",
- .owner = THIS_MODULE,
- .obj_size = sizeof(struct ipx_sock),
-};
-
-static int ipx_create(struct net *net, struct socket *sock, int protocol,
- int kern)
-{
- int rc = -ESOCKTNOSUPPORT;
- struct sock *sk;
-
- if (!net_eq(net, &init_net))
- return -EAFNOSUPPORT;
-
- /*
- * SPX support is not anymore in the kernel sources. If you want to
- * ressurrect it, completing it and making it understand shared skbs,
- * be fully multithreaded, etc, grab the sources in an early 2.5 kernel
- * tree.
- */
- if (sock->type != SOCK_DGRAM)
- goto out;
-
- rc = -ENOMEM;
- sk = sk_alloc(net, PF_IPX, GFP_KERNEL, &ipx_proto, kern);
- if (!sk)
- goto out;
-
- sk_refcnt_debug_inc(sk);
- sock_init_data(sock, sk);
- sk->sk_no_check_tx = 1; /* Checksum off by default */
- sock->ops = &ipx_dgram_ops;
- rc = 0;
-out:
- return rc;
-}
-
-static int ipx_release(struct socket *sock)
-{
- struct sock *sk = sock->sk;
-
- if (!sk)
- goto out;
-
- lock_sock(sk);
- sk->sk_shutdown = SHUTDOWN_MASK;
- if (!sock_flag(sk, SOCK_DEAD))
- sk->sk_state_change(sk);
-
- sock_set_flag(sk, SOCK_DEAD);
- sock->sk = NULL;
- sk_refcnt_debug_release(sk);
- ipx_destroy_socket(sk);
- release_sock(sk);
- sock_put(sk);
-out:
- return 0;
-}
-
-/* caller must hold a reference to intrfc */
-
-static __be16 ipx_first_free_socketnum(struct ipx_interface *intrfc)
-{
- unsigned short socketNum = intrfc->if_sknum;
-
- spin_lock_bh(&intrfc->if_sklist_lock);
-
- if (socketNum < IPX_MIN_EPHEMERAL_SOCKET)
- socketNum = IPX_MIN_EPHEMERAL_SOCKET;
-
- while (__ipxitf_find_socket(intrfc, htons(socketNum)))
- if (socketNum > IPX_MAX_EPHEMERAL_SOCKET)
- socketNum = IPX_MIN_EPHEMERAL_SOCKET;
- else
- socketNum++;
-
- spin_unlock_bh(&intrfc->if_sklist_lock);
- intrfc->if_sknum = socketNum;
-
- return htons(socketNum);
-}
-
-static int __ipx_bind(struct socket *sock,
- struct sockaddr *uaddr, int addr_len)
-{
- struct sock *sk = sock->sk;
- struct ipx_sock *ipxs = ipx_sk(sk);
- struct ipx_interface *intrfc;
- struct sockaddr_ipx *addr = (struct sockaddr_ipx *)uaddr;
- int rc = -EINVAL;
-
- if (!sock_flag(sk, SOCK_ZAPPED) || addr_len != sizeof(struct sockaddr_ipx))
- goto out;
-
- intrfc = ipxitf_find_using_net(addr->sipx_network);
- rc = -EADDRNOTAVAIL;
- if (!intrfc)
- goto out;
-
- if (!addr->sipx_port) {
- addr->sipx_port = ipx_first_free_socketnum(intrfc);
- rc = -EINVAL;
- if (!addr->sipx_port)
- goto out_put;
- }
-
- /* protect IPX system stuff like routing/sap */
- rc = -EACCES;
- if (ntohs(addr->sipx_port) < IPX_MIN_EPHEMERAL_SOCKET &&
- !capable(CAP_NET_ADMIN))
- goto out_put;
-
- ipxs->port = addr->sipx_port;
-
-#ifdef CONFIG_IPX_INTERN
- if (intrfc == ipx_internal_net) {
- /* The source address is to be set explicitly if the
- * socket is to be bound on the internal network. If a
- * node number 0 was specified, the default is used.
- */
-
- rc = -EINVAL;
- if (!memcmp(addr->sipx_node, ipx_broadcast_node, IPX_NODE_LEN))
- goto out_put;
- if (!memcmp(addr->sipx_node, ipx_this_node, IPX_NODE_LEN))
- memcpy(ipxs->node, intrfc->if_node, IPX_NODE_LEN);
- else
- memcpy(ipxs->node, addr->sipx_node, IPX_NODE_LEN);
-
- rc = -EADDRINUSE;
- if (ipxitf_find_internal_socket(intrfc, ipxs->node,
- ipxs->port)) {
- SOCK_DEBUG(sk,
- "IPX: bind failed because port %X in use.\n",
- ntohs(addr->sipx_port));
- goto out_put;
- }
- } else {
- /* Source addresses are easy. It must be our
- * network:node pair for an interface routed to IPX
- * with the ipx routing ioctl()
- */
-
- memcpy(ipxs->node, intrfc->if_node, IPX_NODE_LEN);
-
- rc = -EADDRINUSE;
- if (ipxitf_find_socket(intrfc, addr->sipx_port)) {
- SOCK_DEBUG(sk,
- "IPX: bind failed because port %X in use.\n",
- ntohs(addr->sipx_port));
- goto out_put;
- }
- }
-
-#else /* !def CONFIG_IPX_INTERN */
-
- /* Source addresses are easy. It must be our network:node pair for
- an interface routed to IPX with the ipx routing ioctl() */
-
- rc = -EADDRINUSE;
- if (ipxitf_find_socket(intrfc, addr->sipx_port)) {
- SOCK_DEBUG(sk, "IPX: bind failed because port %X in use.\n",
- ntohs((int)addr->sipx_port));
- goto out_put;
- }
-
-#endif /* CONFIG_IPX_INTERN */
-
- ipxitf_insert_socket(intrfc, sk);
- sock_reset_flag(sk, SOCK_ZAPPED);
-
- rc = 0;
-out_put:
- ipxitf_put(intrfc);
-out:
- return rc;
-}
-
-static int ipx_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
-{
- struct sock *sk = sock->sk;
- int rc;
-
- lock_sock(sk);
- rc = __ipx_bind(sock, uaddr, addr_len);
- release_sock(sk);
-
- return rc;
-}
-
-static int ipx_connect(struct socket *sock, struct sockaddr *uaddr,
- int addr_len, int flags)
-{
- struct sock *sk = sock->sk;
- struct ipx_sock *ipxs = ipx_sk(sk);
- struct sockaddr_ipx *addr;
- int rc = -EINVAL;
- struct ipx_route *rt;
-
- sk->sk_state = TCP_CLOSE;
- sock->state = SS_UNCONNECTED;
-
- lock_sock(sk);
- if (addr_len != sizeof(*addr))
- goto out;
- addr = (struct sockaddr_ipx *)uaddr;
-
- /* put the autobinding in */
- if (!ipxs->port) {
- struct sockaddr_ipx uaddr;
-
- uaddr.sipx_port = 0;
- uaddr.sipx_network = 0;
-
-#ifdef CONFIG_IPX_INTERN
- rc = -ENETDOWN;
- if (!ipxs->intrfc)
- goto out; /* Someone zonked the iface */
- memcpy(uaddr.sipx_node, ipxs->intrfc->if_node,
- IPX_NODE_LEN);
-#endif /* CONFIG_IPX_INTERN */
-
- rc = __ipx_bind(sock, (struct sockaddr *)&uaddr,
- sizeof(struct sockaddr_ipx));
- if (rc)
- goto out;
- }
-
- /* We can either connect to primary network or somewhere
- * we can route to */
- rt = ipxrtr_lookup(addr->sipx_network);
- rc = -ENETUNREACH;
- if (!rt && !(!addr->sipx_network && ipx_primary_net))
- goto out;
-
- ipxs->dest_addr.net = addr->sipx_network;
- ipxs->dest_addr.sock = addr->sipx_port;
- memcpy(ipxs->dest_addr.node, addr->sipx_node, IPX_NODE_LEN);
- ipxs->type = addr->sipx_type;
-
- if (sock->type == SOCK_DGRAM) {
- sock->state = SS_CONNECTED;
- sk->sk_state = TCP_ESTABLISHED;
- }
-
- if (rt)
- ipxrtr_put(rt);
- rc = 0;
-out:
- release_sock(sk);
- return rc;
-}
-
-
-static int ipx_getname(struct socket *sock, struct sockaddr *uaddr,
- int *uaddr_len, int peer)
-{
- struct ipx_address *addr;
- struct sockaddr_ipx sipx;
- struct sock *sk = sock->sk;
- struct ipx_sock *ipxs = ipx_sk(sk);
- int rc;
-
- *uaddr_len = sizeof(struct sockaddr_ipx);
-
- lock_sock(sk);
- if (peer) {
- rc = -ENOTCONN;
- if (sk->sk_state != TCP_ESTABLISHED)
- goto out;
-
- addr = &ipxs->dest_addr;
- sipx.sipx_network = addr->net;
- sipx.sipx_port = addr->sock;
- memcpy(sipx.sipx_node, addr->node, IPX_NODE_LEN);
- } else {
- if (ipxs->intrfc) {
- sipx.sipx_network = ipxs->intrfc->if_netnum;
-#ifdef CONFIG_IPX_INTERN
- memcpy(sipx.sipx_node, ipxs->node, IPX_NODE_LEN);
-#else
- memcpy(sipx.sipx_node, ipxs->intrfc->if_node,
- IPX_NODE_LEN);
-#endif /* CONFIG_IPX_INTERN */
-
- } else {
- sipx.sipx_network = 0;
- memset(sipx.sipx_node, '\0', IPX_NODE_LEN);
- }
-
- sipx.sipx_port = ipxs->port;
- }
-
- sipx.sipx_family = AF_IPX;
- sipx.sipx_type = ipxs->type;
- sipx.sipx_zero = 0;
- memcpy(uaddr, &sipx, sizeof(sipx));
-
- rc = 0;
-out:
- release_sock(sk);
- return rc;
-}
-
-static int ipx_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)
-{
- /* NULL here for pt means the packet was looped back */
- struct ipx_interface *intrfc;
- struct ipxhdr *ipx;
- u16 ipx_pktsize;
- int rc = 0;
-
- if (!net_eq(dev_net(dev), &init_net))
- goto drop;
-
- /* Not ours */
- if (skb->pkt_type == PACKET_OTHERHOST)
- goto drop;
-
- if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL)
- goto out;
-
- if (!pskb_may_pull(skb, sizeof(struct ipxhdr)))
- goto drop;
-
- ipx_pktsize = ntohs(ipx_hdr(skb)->ipx_pktsize);
-
- /* Too small or invalid header? */
- if (ipx_pktsize < sizeof(struct ipxhdr) ||
- !pskb_may_pull(skb, ipx_pktsize))
- goto drop;
-
- ipx = ipx_hdr(skb);
- if (ipx->ipx_checksum != IPX_NO_CHECKSUM &&
- ipx->ipx_checksum != ipx_cksum(ipx, ipx_pktsize))
- goto drop;
-
- IPX_SKB_CB(skb)->ipx_tctrl = ipx->ipx_tctrl;
- IPX_SKB_CB(skb)->ipx_dest_net = ipx->ipx_dest.net;
- IPX_SKB_CB(skb)->ipx_source_net = ipx->ipx_source.net;
-
- /* Determine what local ipx endpoint this is */
- intrfc = ipxitf_find_using_phys(dev, pt->type);
- if (!intrfc) {
- if (ipxcfg_auto_create_interfaces &&
- IPX_SKB_CB(skb)->ipx_dest_net) {
- intrfc = ipxitf_auto_create(dev, pt->type);
- if (intrfc)
- ipxitf_hold(intrfc);
- }
-
- if (!intrfc) /* Not one of ours */
- /* or invalid packet for auto creation */
- goto drop;
- }
-
- rc = ipxitf_rcv(intrfc, skb);
- ipxitf_put(intrfc);
- goto out;
-drop:
- kfree_skb(skb);
-out:
- return rc;
-}
-
-static int ipx_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
-{
- struct sock *sk = sock->sk;
- struct ipx_sock *ipxs = ipx_sk(sk);
- DECLARE_SOCKADDR(struct sockaddr_ipx *, usipx, msg->msg_name);
- struct sockaddr_ipx local_sipx;
- int rc = -EINVAL;
- int flags = msg->msg_flags;
-
- lock_sock(sk);
- /* Socket gets bound below anyway */
-/* if (sk->sk_zapped)
- return -EIO; */ /* Socket not bound */
- if (flags & ~(MSG_DONTWAIT|MSG_CMSG_COMPAT))
- goto out;
-
- /* Max possible packet size limited by 16 bit pktsize in header */
- if (len >= 65535 - sizeof(struct ipxhdr))
- goto out;
-
- if (usipx) {
- if (!ipxs->port) {
- struct sockaddr_ipx uaddr;
-
- uaddr.sipx_port = 0;
- uaddr.sipx_network = 0;
-#ifdef CONFIG_IPX_INTERN
- rc = -ENETDOWN;
- if (!ipxs->intrfc)
- goto out; /* Someone zonked the iface */
- memcpy(uaddr.sipx_node, ipxs->intrfc->if_node,
- IPX_NODE_LEN);
-#endif
- rc = __ipx_bind(sock, (struct sockaddr *)&uaddr,
- sizeof(struct sockaddr_ipx));
- if (rc)
- goto out;
- }
-
- rc = -EINVAL;
- if (msg->msg_namelen < sizeof(*usipx) ||
- usipx->sipx_family != AF_IPX)
- goto out;
- } else {
- rc = -ENOTCONN;
- if (sk->sk_state != TCP_ESTABLISHED)
- goto out;
-
- usipx = &local_sipx;
- usipx->sipx_family = AF_IPX;
- usipx->sipx_type = ipxs->type;
- usipx->sipx_port = ipxs->dest_addr.sock;
- usipx->sipx_network = ipxs->dest_addr.net;
- memcpy(usipx->sipx_node, ipxs->dest_addr.node, IPX_NODE_LEN);
- }
-
- rc = ipxrtr_route_packet(sk, usipx, msg, len, flags & MSG_DONTWAIT);
- if (rc >= 0)
- rc = len;
-out:
- release_sock(sk);
- return rc;
-}
-
-
-static int ipx_recvmsg(struct socket *sock, struct msghdr *msg, size_t size,
- int flags)
-{
- struct sock *sk = sock->sk;
- struct ipx_sock *ipxs = ipx_sk(sk);
- DECLARE_SOCKADDR(struct sockaddr_ipx *, sipx, msg->msg_name);
- struct ipxhdr *ipx = NULL;
- struct sk_buff *skb;
- int copied, rc;
- bool locked = true;
-
- lock_sock(sk);
- /* put the autobinding in */
- if (!ipxs->port) {
- struct sockaddr_ipx uaddr;
-
- uaddr.sipx_port = 0;
- uaddr.sipx_network = 0;
-
-#ifdef CONFIG_IPX_INTERN
- rc = -ENETDOWN;
- if (!ipxs->intrfc)
- goto out; /* Someone zonked the iface */
- memcpy(uaddr.sipx_node, ipxs->intrfc->if_node, IPX_NODE_LEN);
-#endif /* CONFIG_IPX_INTERN */
-
- rc = __ipx_bind(sock, (struct sockaddr *)&uaddr,
- sizeof(struct sockaddr_ipx));
- if (rc)
- goto out;
- }
-
- rc = -ENOTCONN;
- if (sock_flag(sk, SOCK_ZAPPED))
- goto out;
-
- release_sock(sk);
- locked = false;
- skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT,
- flags & MSG_DONTWAIT, &rc);
- if (!skb) {
- if (rc == -EAGAIN && (sk->sk_shutdown & RCV_SHUTDOWN))
- rc = 0;
- goto out;
- }
-
- ipx = ipx_hdr(skb);
- copied = ntohs(ipx->ipx_pktsize) - sizeof(struct ipxhdr);
- if (copied > size) {
- copied = size;
- msg->msg_flags |= MSG_TRUNC;
- }
-
- rc = skb_copy_datagram_msg(skb, sizeof(struct ipxhdr), msg, copied);
- if (rc)
- goto out_free;
- if (skb->tstamp)
- sk->sk_stamp = skb->tstamp;
-
- if (sipx) {
- sipx->sipx_family = AF_IPX;
- sipx->sipx_port = ipx->ipx_source.sock;
- memcpy(sipx->sipx_node, ipx->ipx_source.node, IPX_NODE_LEN);
- sipx->sipx_network = IPX_SKB_CB(skb)->ipx_source_net;
- sipx->sipx_type = ipx->ipx_type;
- sipx->sipx_zero = 0;
- msg->msg_namelen = sizeof(*sipx);
- }
- rc = copied;
-
-out_free:
- skb_free_datagram(sk, skb);
-out:
- if (locked)
- release_sock(sk);
- return rc;
-}
-
-
-static int ipx_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
-{
- int rc = 0;
- long amount = 0;
- struct sock *sk = sock->sk;
- void __user *argp = (void __user *)arg;
-
- lock_sock(sk);
- switch (cmd) {
- case TIOCOUTQ:
- amount = sk->sk_sndbuf - sk_wmem_alloc_get(sk);
- if (amount < 0)
- amount = 0;
- rc = put_user(amount, (int __user *)argp);
- break;
- case TIOCINQ: {
- struct sk_buff *skb = skb_peek(&sk->sk_receive_queue);
- /* These two are safe on a single CPU system as only
- * user tasks fiddle here */
- if (skb)
- amount = skb->len - sizeof(struct ipxhdr);
- rc = put_user(amount, (int __user *)argp);
- break;
- }
- case SIOCADDRT:
- case SIOCDELRT:
- rc = -EPERM;
- if (capable(CAP_NET_ADMIN))
- rc = ipxrtr_ioctl(cmd, argp);
- break;
- case SIOCSIFADDR:
- case SIOCAIPXITFCRT:
- case SIOCAIPXPRISLT:
- rc = -EPERM;
- if (!capable(CAP_NET_ADMIN))
- break;
- /* fall through */
- case SIOCGIFADDR:
- rc = ipxitf_ioctl(cmd, argp);
- break;
- case SIOCIPXCFGDATA:
- rc = ipxcfg_get_config_data(argp);
- break;
- case SIOCIPXNCPCONN:
- /*
- * This socket wants to take care of the NCP connection
- * handed to us in arg.
- */
- rc = -EPERM;
- if (!capable(CAP_NET_ADMIN))
- break;
- rc = get_user(ipx_sk(sk)->ipx_ncp_conn,
- (const unsigned short __user *)argp);
- break;
- case SIOCGSTAMP:
- rc = sock_get_timestamp(sk, argp);
- break;
- case SIOCGIFDSTADDR:
- case SIOCSIFDSTADDR:
- case SIOCGIFBRDADDR:
- case SIOCSIFBRDADDR:
- case SIOCGIFNETMASK:
- case SIOCSIFNETMASK:
- rc = -EINVAL;
- break;
- default:
- rc = -ENOIOCTLCMD;
- break;
- }
- release_sock(sk);
-
- return rc;
-}
-
-
-#ifdef CONFIG_COMPAT
-static int ipx_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
-{
- /*
- * These 4 commands use same structure on 32bit and 64bit. Rest of IPX
- * commands is handled by generic ioctl code. As these commands are
- * SIOCPROTOPRIVATE..SIOCPROTOPRIVATE+3, they cannot be handled by generic
- * code.
- */
- switch (cmd) {
- case SIOCAIPXITFCRT:
- case SIOCAIPXPRISLT:
- case SIOCIPXCFGDATA:
- case SIOCIPXNCPCONN:
- return ipx_ioctl(sock, cmd, arg);
- default:
- return -ENOIOCTLCMD;
- }
-}
-#endif
-
-static int ipx_shutdown(struct socket *sock, int mode)
-{
- struct sock *sk = sock->sk;
-
- if (mode < SHUT_RD || mode > SHUT_RDWR)
- return -EINVAL;
- /* This maps:
- * SHUT_RD (0) -> RCV_SHUTDOWN (1)
- * SHUT_WR (1) -> SEND_SHUTDOWN (2)
- * SHUT_RDWR (2) -> SHUTDOWN_MASK (3)
- */
- ++mode;
-
- lock_sock(sk);
- sk->sk_shutdown |= mode;
- release_sock(sk);
- sk->sk_state_change(sk);
-
- return 0;
-}
-
-/*
- * Socket family declarations
- */
-
-static const struct net_proto_family ipx_family_ops = {
- .family = PF_IPX,
- .create = ipx_create,
- .owner = THIS_MODULE,
-};
-
-static const struct proto_ops ipx_dgram_ops = {
- .family = PF_IPX,
- .owner = THIS_MODULE,
- .release = ipx_release,
- .bind = ipx_bind,
- .connect = ipx_connect,
- .socketpair = sock_no_socketpair,
- .accept = sock_no_accept,
- .getname = ipx_getname,
- .poll = datagram_poll,
- .ioctl = ipx_ioctl,
-#ifdef CONFIG_COMPAT
- .compat_ioctl = ipx_compat_ioctl,
-#endif
- .listen = sock_no_listen,
- .shutdown = ipx_shutdown,
- .setsockopt = ipx_setsockopt,
- .getsockopt = ipx_getsockopt,
- .sendmsg = ipx_sendmsg,
- .recvmsg = ipx_recvmsg,
- .mmap = sock_no_mmap,
- .sendpage = sock_no_sendpage,
-};
-
-static struct packet_type ipx_8023_packet_type __read_mostly = {
- .type = cpu_to_be16(ETH_P_802_3),
- .func = ipx_rcv,
-};
-
-static struct packet_type ipx_dix_packet_type __read_mostly = {
- .type = cpu_to_be16(ETH_P_IPX),
- .func = ipx_rcv,
-};
-
-static struct notifier_block ipx_dev_notifier = {
- .notifier_call = ipxitf_device_event,
-};
-
-static const unsigned char ipx_8022_type = 0xE0;
-static const unsigned char ipx_snap_id[5] = { 0x0, 0x0, 0x0, 0x81, 0x37 };
-static const char ipx_EII_err_msg[] __initconst =
- KERN_CRIT "IPX: Unable to register with Ethernet II\n";
-static const char ipx_8023_err_msg[] __initconst =
- KERN_CRIT "IPX: Unable to register with 802.3\n";
-static const char ipx_llc_err_msg[] __initconst =
- KERN_CRIT "IPX: Unable to register with 802.2\n";
-static const char ipx_snap_err_msg[] __initconst =
- KERN_CRIT "IPX: Unable to register with SNAP\n";
-
-static int __init ipx_init(void)
-{
- int rc = proto_register(&ipx_proto, 1);
-
- if (rc != 0)
- goto out;
-
- sock_register(&ipx_family_ops);
-
- pEII_datalink = make_EII_client();
- if (pEII_datalink)
- dev_add_pack(&ipx_dix_packet_type);
- else
- printk(ipx_EII_err_msg);
-
- p8023_datalink = make_8023_client();
- if (p8023_datalink)
- dev_add_pack(&ipx_8023_packet_type);
- else
- printk(ipx_8023_err_msg);
-
- p8022_datalink = register_8022_client(ipx_8022_type, ipx_rcv);
- if (!p8022_datalink)
- printk(ipx_llc_err_msg);
-
- pSNAP_datalink = register_snap_client(ipx_snap_id, ipx_rcv);
- if (!pSNAP_datalink)
- printk(ipx_snap_err_msg);
-
- register_netdevice_notifier(&ipx_dev_notifier);
- ipx_register_sysctl();
- ipx_proc_init();
-out:
- return rc;
-}
-
-static void __exit ipx_proto_finito(void)
-{
- ipx_proc_exit();
- ipx_unregister_sysctl();
-
- unregister_netdevice_notifier(&ipx_dev_notifier);
-
- ipxitf_cleanup();
-
- if (pSNAP_datalink) {
- unregister_snap_client(pSNAP_datalink);
- pSNAP_datalink = NULL;
- }
-
- if (p8022_datalink) {
- unregister_8022_client(p8022_datalink);
- p8022_datalink = NULL;
- }
-
- dev_remove_pack(&ipx_8023_packet_type);
- if (p8023_datalink) {
- destroy_8023_client(p8023_datalink);
- p8023_datalink = NULL;
- }
-
- dev_remove_pack(&ipx_dix_packet_type);
- if (pEII_datalink) {
- destroy_EII_client(pEII_datalink);
- pEII_datalink = NULL;
- }
-
- proto_unregister(&ipx_proto);
- sock_unregister(ipx_family_ops.family);
-}
-
-module_init(ipx_init);
-module_exit(ipx_proto_finito);
-MODULE_LICENSE("GPL");
-MODULE_ALIAS_NETPROTO(PF_IPX);
diff --git a/net/ipx/ipx_proc.c b/net/ipx/ipx_proc.c
deleted file mode 100644
index b9232e4e2ed4..000000000000
--- a/net/ipx/ipx_proc.c
+++ /dev/null
@@ -1,338 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * IPX proc routines
- *
- * Copyright(C) Arnaldo Carvalho de Melo <acme@conectiva.com.br>, 2002
- */
-
-#include <linux/init.h>
-#ifdef CONFIG_PROC_FS
-#include <linux/proc_fs.h>
-#include <linux/spinlock.h>
-#include <linux/seq_file.h>
-#include <linux/export.h>
-#include <net/net_namespace.h>
-#include <net/tcp_states.h>
-#include <net/ipx.h>
-
-static void *ipx_seq_interface_start(struct seq_file *seq, loff_t *pos)
-{
- spin_lock_bh(&ipx_interfaces_lock);
- return seq_list_start_head(&ipx_interfaces, *pos);
-}
-
-static void *ipx_seq_interface_next(struct seq_file *seq, void *v, loff_t *pos)
-{
- return seq_list_next(v, &ipx_interfaces, pos);
-}
-
-static void ipx_seq_interface_stop(struct seq_file *seq, void *v)
-{
- spin_unlock_bh(&ipx_interfaces_lock);
-}
-
-static int ipx_seq_interface_show(struct seq_file *seq, void *v)
-{
- struct ipx_interface *i;
-
- if (v == &ipx_interfaces) {
- seq_puts(seq, "Network Node_Address Primary Device "
- "Frame_Type");
-#ifdef IPX_REFCNT_DEBUG
- seq_puts(seq, " refcnt");
-#endif
- seq_puts(seq, "\n");
- goto out;
- }
-
- i = list_entry(v, struct ipx_interface, node);
- seq_printf(seq, "%08X ", ntohl(i->if_netnum));
- seq_printf(seq, "%02X%02X%02X%02X%02X%02X ",
- i->if_node[0], i->if_node[1], i->if_node[2],
- i->if_node[3], i->if_node[4], i->if_node[5]);
- seq_printf(seq, "%-9s", i == ipx_primary_net ? "Yes" : "No");
- seq_printf(seq, "%-11s", ipx_device_name(i));
- seq_printf(seq, "%-9s", ipx_frame_name(i->if_dlink_type));
-#ifdef IPX_REFCNT_DEBUG
- seq_printf(seq, "%6d", refcount_read(&i->refcnt));
-#endif
- seq_puts(seq, "\n");
-out:
- return 0;
-}
-
-static void *ipx_seq_route_start(struct seq_file *seq, loff_t *pos)
-{
- read_lock_bh(&ipx_routes_lock);
- return seq_list_start_head(&ipx_routes, *pos);
-}
-
-static void *ipx_seq_route_next(struct seq_file *seq, void *v, loff_t *pos)
-{
- return seq_list_next(v, &ipx_routes, pos);
-}
-
-static void ipx_seq_route_stop(struct seq_file *seq, void *v)
-{
- read_unlock_bh(&ipx_routes_lock);
-}
-
-static int ipx_seq_route_show(struct seq_file *seq, void *v)
-{
- struct ipx_route *rt;
-
- if (v == &ipx_routes) {
- seq_puts(seq, "Network Router_Net Router_Node\n");
- goto out;
- }
-
- rt = list_entry(v, struct ipx_route, node);
-
- seq_printf(seq, "%08X ", ntohl(rt->ir_net));
- if (rt->ir_routed)
- seq_printf(seq, "%08X %02X%02X%02X%02X%02X%02X\n",
- ntohl(rt->ir_intrfc->if_netnum),
- rt->ir_router_node[0], rt->ir_router_node[1],
- rt->ir_router_node[2], rt->ir_router_node[3],
- rt->ir_router_node[4], rt->ir_router_node[5]);
- else
- seq_puts(seq, "Directly Connected\n");
-out:
- return 0;
-}
-
-static __inline__ struct sock *ipx_get_socket_idx(loff_t pos)
-{
- struct sock *s = NULL;
- struct ipx_interface *i;
-
- list_for_each_entry(i, &ipx_interfaces, node) {
- spin_lock_bh(&i->if_sklist_lock);
- sk_for_each(s, &i->if_sklist) {
- if (!pos)
- break;
- --pos;
- }
- spin_unlock_bh(&i->if_sklist_lock);
- if (!pos) {
- if (s)
- goto found;
- break;
- }
- }
- s = NULL;
-found:
- return s;
-}
-
-static void *ipx_seq_socket_start(struct seq_file *seq, loff_t *pos)
-{
- loff_t l = *pos;
-
- spin_lock_bh(&ipx_interfaces_lock);
- return l ? ipx_get_socket_idx(--l) : SEQ_START_TOKEN;
-}
-
-static void *ipx_seq_socket_next(struct seq_file *seq, void *v, loff_t *pos)
-{
- struct sock* sk, *next;
- struct ipx_interface *i;
- struct ipx_sock *ipxs;
-
- ++*pos;
- if (v == SEQ_START_TOKEN) {
- sk = NULL;
- i = ipx_interfaces_head();
- if (!i)
- goto out;
- sk = sk_head(&i->if_sklist);
- if (sk)
- spin_lock_bh(&i->if_sklist_lock);
- goto out;
- }
- sk = v;
- next = sk_next(sk);
- if (next) {
- sk = next;
- goto out;
- }
- ipxs = ipx_sk(sk);
- i = ipxs->intrfc;
- spin_unlock_bh(&i->if_sklist_lock);
- sk = NULL;
- for (;;) {
- if (i->node.next == &ipx_interfaces)
- break;
- i = list_entry(i->node.next, struct ipx_interface, node);
- spin_lock_bh(&i->if_sklist_lock);
- if (!hlist_empty(&i->if_sklist)) {
- sk = sk_head(&i->if_sklist);
- break;
- }
- spin_unlock_bh(&i->if_sklist_lock);
- }
-out:
- return sk;
-}
-
-static int ipx_seq_socket_show(struct seq_file *seq, void *v)
-{
- struct sock *s;
- struct ipx_sock *ipxs;
-
- if (v == SEQ_START_TOKEN) {
-#ifdef CONFIG_IPX_INTERN
- seq_puts(seq, "Local_Address "
- "Remote_Address Tx_Queue "
- "Rx_Queue State Uid\n");
-#else
- seq_puts(seq, "Local_Address Remote_Address "
- "Tx_Queue Rx_Queue State Uid\n");
-#endif
- goto out;
- }
-
- s = v;
- ipxs = ipx_sk(s);
-#ifdef CONFIG_IPX_INTERN
- seq_printf(seq, "%08X:%02X%02X%02X%02X%02X%02X:%04X ",
- ntohl(ipxs->intrfc->if_netnum),
- ipxs->node[0], ipxs->node[1], ipxs->node[2], ipxs->node[3],
- ipxs->node[4], ipxs->node[5], ntohs(ipxs->port));
-#else
- seq_printf(seq, "%08X:%04X ", ntohl(ipxs->intrfc->if_netnum),
- ntohs(ipxs->port));
-#endif /* CONFIG_IPX_INTERN */
- if (s->sk_state != TCP_ESTABLISHED)
- seq_printf(seq, "%-28s", "Not_Connected");
- else {
- seq_printf(seq, "%08X:%02X%02X%02X%02X%02X%02X:%04X ",
- ntohl(ipxs->dest_addr.net),
- ipxs->dest_addr.node[0], ipxs->dest_addr.node[1],
- ipxs->dest_addr.node[2], ipxs->dest_addr.node[3],
- ipxs->dest_addr.node[4], ipxs->dest_addr.node[5],
- ntohs(ipxs->dest_addr.sock));
- }
-
- seq_printf(seq, "%08X %08X %02X %03u\n",
- sk_wmem_alloc_get(s),
- sk_rmem_alloc_get(s),
- s->sk_state,
- from_kuid_munged(seq_user_ns(seq), sock_i_uid(s)));
-out:
- return 0;
-}
-
-static const struct seq_operations ipx_seq_interface_ops = {
- .start = ipx_seq_interface_start,
- .next = ipx_seq_interface_next,
- .stop = ipx_seq_interface_stop,
- .show = ipx_seq_interface_show,
-};
-
-static const struct seq_operations ipx_seq_route_ops = {
- .start = ipx_seq_route_start,
- .next = ipx_seq_route_next,
- .stop = ipx_seq_route_stop,
- .show = ipx_seq_route_show,
-};
-
-static const struct seq_operations ipx_seq_socket_ops = {
- .start = ipx_seq_socket_start,
- .next = ipx_seq_socket_next,
- .stop = ipx_seq_interface_stop,
- .show = ipx_seq_socket_show,
-};
-
-static int ipx_seq_route_open(struct inode *inode, struct file *file)
-{
- return seq_open(file, &ipx_seq_route_ops);
-}
-
-static int ipx_seq_interface_open(struct inode *inode, struct file *file)
-{
- return seq_open(file, &ipx_seq_interface_ops);
-}
-
-static int ipx_seq_socket_open(struct inode *inode, struct file *file)
-{
- return seq_open(file, &ipx_seq_socket_ops);
-}
-
-static const struct file_operations ipx_seq_interface_fops = {
- .open = ipx_seq_interface_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = seq_release,
-};
-
-static const struct file_operations ipx_seq_route_fops = {
- .open = ipx_seq_route_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = seq_release,
-};
-
-static const struct file_operations ipx_seq_socket_fops = {
- .open = ipx_seq_socket_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = seq_release,
-};
-
-static struct proc_dir_entry *ipx_proc_dir;
-
-int __init ipx_proc_init(void)
-{
- struct proc_dir_entry *p;
- int rc = -ENOMEM;
-
- ipx_proc_dir = proc_mkdir("ipx", init_net.proc_net);
-
- if (!ipx_proc_dir)
- goto out;
- p = proc_create("interface", S_IRUGO,
- ipx_proc_dir, &ipx_seq_interface_fops);
- if (!p)
- goto out_interface;
-
- p = proc_create("route", S_IRUGO, ipx_proc_dir, &ipx_seq_route_fops);
- if (!p)
- goto out_route;
-
- p = proc_create("socket", S_IRUGO, ipx_proc_dir, &ipx_seq_socket_fops);
- if (!p)
- goto out_socket;
-
- rc = 0;
-out:
- return rc;
-out_socket:
- remove_proc_entry("route", ipx_proc_dir);
-out_route:
- remove_proc_entry("interface", ipx_proc_dir);
-out_interface:
- remove_proc_entry("ipx", init_net.proc_net);
- goto out;
-}
-
-void __exit ipx_proc_exit(void)
-{
- remove_proc_entry("interface", ipx_proc_dir);
- remove_proc_entry("route", ipx_proc_dir);
- remove_proc_entry("socket", ipx_proc_dir);
- remove_proc_entry("ipx", init_net.proc_net);
-}
-
-#else /* CONFIG_PROC_FS */
-
-int __init ipx_proc_init(void)
-{
- return 0;
-}
-
-void __exit ipx_proc_exit(void)
-{
-}
-
-#endif /* CONFIG_PROC_FS */
diff --git a/net/ipx/ipx_route.c b/net/ipx/ipx_route.c
deleted file mode 100644
index 3cf93aa9f284..000000000000
--- a/net/ipx/ipx_route.c
+++ /dev/null
@@ -1,293 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Implements the IPX routing routines.
- * Code moved from af_ipx.c.
- *
- * Arnaldo Carvalho de Melo <acme@conectiva.com.br>, 2003
- *
- * See net/ipx/ChangeLog.
- */
-
-#include <linux/list.h>
-#include <linux/route.h>
-#include <linux/slab.h>
-#include <linux/spinlock.h>
-
-#include <net/ipx.h>
-#include <net/sock.h>
-
-LIST_HEAD(ipx_routes);
-DEFINE_RWLOCK(ipx_routes_lock);
-
-extern struct ipx_interface *ipx_internal_net;
-
-extern struct ipx_interface *ipxitf_find_using_net(__be32 net);
-extern int ipxitf_demux_socket(struct ipx_interface *intrfc,
- struct sk_buff *skb, int copy);
-extern int ipxitf_demux_socket(struct ipx_interface *intrfc,
- struct sk_buff *skb, int copy);
-
-struct ipx_route *ipxrtr_lookup(__be32 net)
-{
- struct ipx_route *r;
-
- read_lock_bh(&ipx_routes_lock);
- list_for_each_entry(r, &ipx_routes, node)
- if (r->ir_net == net) {
- ipxrtr_hold(r);
- goto unlock;
- }
- r = NULL;
-unlock:
- read_unlock_bh(&ipx_routes_lock);
- return r;
-}
-
-/*
- * Caller must hold a reference to intrfc
- */
-int ipxrtr_add_route(__be32 network, struct ipx_interface *intrfc,
- unsigned char *node)
-{
- struct ipx_route *rt;
- int rc;
-
- /* Get a route structure; either existing or create */
- rt = ipxrtr_lookup(network);
- if (!rt) {
- rt = kmalloc(sizeof(*rt), GFP_ATOMIC);
- rc = -EAGAIN;
- if (!rt)
- goto out;
-
- refcount_set(&rt->refcnt, 1);
- ipxrtr_hold(rt);
- write_lock_bh(&ipx_routes_lock);
- list_add(&rt->node, &ipx_routes);
- write_unlock_bh(&ipx_routes_lock);
- } else {
- rc = -EEXIST;
- if (intrfc == ipx_internal_net)
- goto out_put;
- }
-
- rt->ir_net = network;
- rt->ir_intrfc = intrfc;
- if (!node) {
- memset(rt->ir_router_node, '\0', IPX_NODE_LEN);
- rt->ir_routed = 0;
- } else {
- memcpy(rt->ir_router_node, node, IPX_NODE_LEN);
- rt->ir_routed = 1;
- }
-
- rc = 0;
-out_put:
- ipxrtr_put(rt);
-out:
- return rc;
-}
-
-void ipxrtr_del_routes(struct ipx_interface *intrfc)
-{
- struct ipx_route *r, *tmp;
-
- write_lock_bh(&ipx_routes_lock);
- list_for_each_entry_safe(r, tmp, &ipx_routes, node)
- if (r->ir_intrfc == intrfc) {
- list_del(&r->node);
- ipxrtr_put(r);
- }
- write_unlock_bh(&ipx_routes_lock);
-}
-
-static int ipxrtr_create(struct ipx_route_definition *rd)
-{
- struct ipx_interface *intrfc;
- int rc = -ENETUNREACH;
-
- /* Find the appropriate interface */
- intrfc = ipxitf_find_using_net(rd->ipx_router_network);
- if (!intrfc)
- goto out;
- rc = ipxrtr_add_route(rd->ipx_network, intrfc, rd->ipx_router_node);
- ipxitf_put(intrfc);
-out:
- return rc;
-}
-
-static int ipxrtr_delete(__be32 net)
-{
- struct ipx_route *r, *tmp;
- int rc;
-
- write_lock_bh(&ipx_routes_lock);
- list_for_each_entry_safe(r, tmp, &ipx_routes, node)
- if (r->ir_net == net) {
- /* Directly connected; can't lose route */
- rc = -EPERM;
- if (!r->ir_routed)
- goto out;
- list_del(&r->node);
- ipxrtr_put(r);
- rc = 0;
- goto out;
- }
- rc = -ENOENT;
-out:
- write_unlock_bh(&ipx_routes_lock);
- return rc;
-}
-
-/*
- * The skb has to be unshared, we'll end up calling ipxitf_send, that'll
- * modify the packet
- */
-int ipxrtr_route_skb(struct sk_buff *skb)
-{
- struct ipxhdr *ipx = ipx_hdr(skb);
- struct ipx_route *r = ipxrtr_lookup(IPX_SKB_CB(skb)->ipx_dest_net);
-
- if (!r) { /* no known route */
- kfree_skb(skb);
- return 0;
- }
-
- ipxitf_hold(r->ir_intrfc);
- ipxitf_send(r->ir_intrfc, skb, r->ir_routed ?
- r->ir_router_node : ipx->ipx_dest.node);
- ipxitf_put(r->ir_intrfc);
- ipxrtr_put(r);
-
- return 0;
-}
-
-/*
- * Route an outgoing frame from a socket.
- */
-int ipxrtr_route_packet(struct sock *sk, struct sockaddr_ipx *usipx,
- struct msghdr *msg, size_t len, int noblock)
-{
- struct sk_buff *skb;
- struct ipx_sock *ipxs = ipx_sk(sk);
- struct ipx_interface *intrfc;
- struct ipxhdr *ipx;
- size_t size;
- int ipx_offset;
- struct ipx_route *rt = NULL;
- int rc;
-
- /* Find the appropriate interface on which to send packet */
- if (!usipx->sipx_network && ipx_primary_net) {
- usipx->sipx_network = ipx_primary_net->if_netnum;
- intrfc = ipx_primary_net;
- } else {
- rt = ipxrtr_lookup(usipx->sipx_network);
- rc = -ENETUNREACH;
- if (!rt)
- goto out;
- intrfc = rt->ir_intrfc;
- }
-
- ipxitf_hold(intrfc);
- ipx_offset = intrfc->if_ipx_offset;
- size = sizeof(struct ipxhdr) + len + ipx_offset;
-
- skb = sock_alloc_send_skb(sk, size, noblock, &rc);
- if (!skb)
- goto out_put;
-
- skb_reserve(skb, ipx_offset);
- skb->sk = sk;
-
- /* Fill in IPX header */
- skb_reset_network_header(skb);
- skb_reset_transport_header(skb);
- skb_put(skb, sizeof(struct ipxhdr));
- ipx = ipx_hdr(skb);
- ipx->ipx_pktsize = htons(len + sizeof(struct ipxhdr));
- IPX_SKB_CB(skb)->ipx_tctrl = 0;
- ipx->ipx_type = usipx->sipx_type;
-
- IPX_SKB_CB(skb)->last_hop.index = -1;
-#ifdef CONFIG_IPX_INTERN
- IPX_SKB_CB(skb)->ipx_source_net = ipxs->intrfc->if_netnum;
- memcpy(ipx->ipx_source.node, ipxs->node, IPX_NODE_LEN);
-#else
- rc = ntohs(ipxs->port);
- if (rc == 0x453 || rc == 0x452) {
- /* RIP/SAP special handling for mars_nwe */
- IPX_SKB_CB(skb)->ipx_source_net = intrfc->if_netnum;
- memcpy(ipx->ipx_source.node, intrfc->if_node, IPX_NODE_LEN);
- } else {
- IPX_SKB_CB(skb)->ipx_source_net = ipxs->intrfc->if_netnum;
- memcpy(ipx->ipx_source.node, ipxs->intrfc->if_node,
- IPX_NODE_LEN);
- }
-#endif /* CONFIG_IPX_INTERN */
- ipx->ipx_source.sock = ipxs->port;
- IPX_SKB_CB(skb)->ipx_dest_net = usipx->sipx_network;
- memcpy(ipx->ipx_dest.node, usipx->sipx_node, IPX_NODE_LEN);
- ipx->ipx_dest.sock = usipx->sipx_port;
-
- rc = memcpy_from_msg(skb_put(skb, len), msg, len);
- if (rc) {
- kfree_skb(skb);
- goto out_put;
- }
-
- /* Apply checksum. Not allowed on 802.3 links. */
- if (sk->sk_no_check_tx ||
- intrfc->if_dlink_type == htons(IPX_FRAME_8023))
- ipx->ipx_checksum = htons(0xFFFF);
- else
- ipx->ipx_checksum = ipx_cksum(ipx, len + sizeof(struct ipxhdr));
-
- rc = ipxitf_send(intrfc, skb, (rt && rt->ir_routed) ?
- rt->ir_router_node : ipx->ipx_dest.node);
-out_put:
- ipxitf_put(intrfc);
- if (rt)
- ipxrtr_put(rt);
-out:
- return rc;
-}
-
-/*
- * We use a normal struct rtentry for route handling
- */
-int ipxrtr_ioctl(unsigned int cmd, void __user *arg)
-{
- struct rtentry rt; /* Use these to behave like 'other' stacks */
- struct sockaddr_ipx *sg, *st;
- int rc = -EFAULT;
-
- if (copy_from_user(&rt, arg, sizeof(rt)))
- goto out;
-
- sg = (struct sockaddr_ipx *)&rt.rt_gateway;
- st = (struct sockaddr_ipx *)&rt.rt_dst;
-
- rc = -EINVAL;
- if (!(rt.rt_flags & RTF_GATEWAY) || /* Direct routes are fixed */
- sg->sipx_family != AF_IPX ||
- st->sipx_family != AF_IPX)
- goto out;
-
- switch (cmd) {
- case SIOCDELRT:
- rc = ipxrtr_delete(st->sipx_network);
- break;
- case SIOCADDRT: {
- struct ipx_route_definition f;
- f.ipx_network = st->sipx_network;
- f.ipx_router_network = sg->sipx_network;
- memcpy(f.ipx_router_node, sg->sipx_node, IPX_NODE_LEN);
- rc = ipxrtr_create(&f);
- break;
- }
- }
-
-out:
- return rc;
-}
diff --git a/net/ipx/pe2.c b/net/ipx/pe2.c
deleted file mode 100644
index ba7d4214bbff..000000000000
--- a/net/ipx/pe2.c
+++ /dev/null
@@ -1,36 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include <linux/in.h>
-#include <linux/mm.h>
-#include <linux/module.h>
-#include <linux/netdevice.h>
-#include <linux/skbuff.h>
-#include <linux/slab.h>
-
-#include <net/datalink.h>
-
-static int pEII_request(struct datalink_proto *dl,
- struct sk_buff *skb, unsigned char *dest_node)
-{
- struct net_device *dev = skb->dev;
-
- skb->protocol = htons(ETH_P_IPX);
- dev_hard_header(skb, dev, ETH_P_IPX, dest_node, NULL, skb->len);
- return dev_queue_xmit(skb);
-}
-
-struct datalink_proto *make_EII_client(void)
-{
- struct datalink_proto *proto = kmalloc(sizeof(*proto), GFP_ATOMIC);
-
- if (proto) {
- proto->header_length = 0;
- proto->request = pEII_request;
- }
-
- return proto;
-}
-
-void destroy_EII_client(struct datalink_proto *dl)
-{
- kfree(dl);
-}
diff --git a/net/ipx/sysctl_net_ipx.c b/net/ipx/sysctl_net_ipx.c
deleted file mode 100644
index c3eef457db88..000000000000
--- a/net/ipx/sysctl_net_ipx.c
+++ /dev/null
@@ -1,40 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/* -*- linux-c -*-
- * sysctl_net_ipx.c: sysctl interface to net IPX subsystem.
- *
- * Begun April 1, 1996, Mike Shaver.
- * Added /proc/sys/net/ipx directory entry (empty =) ). [MS]
- * Added /proc/sys/net/ipx/ipx_pprop_broadcasting - acme March 4, 2001
- */
-
-#include <linux/mm.h>
-#include <linux/sysctl.h>
-#include <net/net_namespace.h>
-#include <net/ipx.h>
-
-#ifndef CONFIG_SYSCTL
-#error This file should not be compiled without CONFIG_SYSCTL defined
-#endif
-
-static struct ctl_table ipx_table[] = {
- {
- .procname = "ipx_pprop_broadcasting",
- .data = &sysctl_ipx_pprop_broadcasting,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec,
- },
- { },
-};
-
-static struct ctl_table_header *ipx_table_header;
-
-void ipx_register_sysctl(void)
-{
- ipx_table_header = register_net_sysctl(&init_net, "net/ipx", ipx_table);
-}
-
-void ipx_unregister_sysctl(void)
-{
- unregister_net_sysctl_table(ipx_table_header);
-}
diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c
index 5dce8336d33f..e545a3c9365f 100644
--- a/net/mpls/af_mpls.c
+++ b/net/mpls/af_mpls.c
@@ -8,6 +8,7 @@
#include <linux/ipv6.h>
#include <linux/mpls.h>
#include <linux/netconf.h>
+#include <linux/nospec.h>
#include <linux/vmalloc.h>
#include <linux/percpu.h>
#include <net/ip.h>
@@ -935,24 +936,27 @@ errout:
return err;
}
-static bool mpls_label_ok(struct net *net, unsigned int index,
+static bool mpls_label_ok(struct net *net, unsigned int *index,
struct netlink_ext_ack *extack)
{
+ bool is_ok = true;
+
/* Reserved labels may not be set */
- if (index < MPLS_LABEL_FIRST_UNRESERVED) {
+ if (*index < MPLS_LABEL_FIRST_UNRESERVED) {
NL_SET_ERR_MSG(extack,
"Invalid label - must be MPLS_LABEL_FIRST_UNRESERVED or higher");
- return false;
+ is_ok = false;
}
/* The full 20 bit range may not be supported. */
- if (index >= net->mpls.platform_labels) {
+ if (is_ok && *index >= net->mpls.platform_labels) {
NL_SET_ERR_MSG(extack,
"Label >= configured maximum in platform_labels");
- return false;
+ is_ok = false;
}
- return true;
+ *index = array_index_nospec(*index, net->mpls.platform_labels);
+ return is_ok;
}
static int mpls_route_add(struct mpls_route_config *cfg,
@@ -975,7 +979,7 @@ static int mpls_route_add(struct mpls_route_config *cfg,
index = find_free_label(net);
}
- if (!mpls_label_ok(net, index, extack))
+ if (!mpls_label_ok(net, &index, extack))
goto errout;
/* Append makes no sense with mpls */
@@ -1052,7 +1056,7 @@ static int mpls_route_del(struct mpls_route_config *cfg,
index = cfg->rc_label;
- if (!mpls_label_ok(net, index, extack))
+ if (!mpls_label_ok(net, &index, extack))
goto errout;
mpls_route_update(net, index, NULL, &cfg->rc_nlinfo);
@@ -1810,7 +1814,7 @@ static int rtm_to_route_config(struct sk_buff *skb,
goto errout;
if (!mpls_label_ok(cfg->rc_nlinfo.nl_net,
- cfg->rc_label, extack))
+ &cfg->rc_label, extack))
goto errout;
break;
}
@@ -2137,7 +2141,7 @@ static int mpls_getroute(struct sk_buff *in_skb, struct nlmsghdr *in_nlh,
goto errout;
}
- if (!mpls_label_ok(net, in_label, extack)) {
+ if (!mpls_label_ok(net, &in_label, extack)) {
err = -EINVAL;
goto errout;
}
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
index 9019fa98003d..d3220b43c832 100644
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
@@ -666,8 +666,8 @@ endif # NF_TABLES
config NF_FLOW_TABLE_INET
tristate "Netfilter flow table mixed IPv4/IPv6 module"
- depends on NF_FLOW_TABLE_IPV4 && NF_FLOW_TABLE_IPV6
- select NF_FLOW_TABLE
+ depends on NF_FLOW_TABLE_IPV4
+ depends on NF_FLOW_TABLE_IPV6
help
This option adds the flow table mixed IPv4/IPv6 support.
@@ -675,7 +675,9 @@ config NF_FLOW_TABLE_INET
config NF_FLOW_TABLE
tristate "Netfilter flow table module"
- depends on NF_CONNTRACK && NF_TABLES
+ depends on NETFILTER_INGRESS
+ depends on NF_CONNTRACK
+ depends on NF_TABLES
help
This option adds the flow table core infrastructure.
diff --git a/net/netfilter/ipset/ip_set_hash_ipportnet.c b/net/netfilter/ipset/ip_set_hash_ipportnet.c
index 0f164e986bf1..88b83d6d3084 100644
--- a/net/netfilter/ipset/ip_set_hash_ipportnet.c
+++ b/net/netfilter/ipset/ip_set_hash_ipportnet.c
@@ -168,7 +168,7 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
struct hash_ipportnet4_elem e = { .cidr = HOST_MASK - 1 };
struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
u32 ip = 0, ip_to = 0, p = 0, port, port_to;
- u32 ip2_from = 0, ip2_to = 0, ip2_last, ip2;
+ u32 ip2_from = 0, ip2_to = 0, ip2;
bool with_ports = false;
u8 cidr;
int ret;
@@ -269,22 +269,21 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
ip_set_mask_from_to(ip2_from, ip2_to, e.cidr + 1);
}
- if (retried)
+ if (retried) {
ip = ntohl(h->next.ip);
+ p = ntohs(h->next.port);
+ ip2 = ntohl(h->next.ip2);
+ } else {
+ p = port;
+ ip2 = ip2_from;
+ }
for (; ip <= ip_to; ip++) {
e.ip = htonl(ip);
- p = retried && ip == ntohl(h->next.ip) ? ntohs(h->next.port)
- : port;
for (; p <= port_to; p++) {
e.port = htons(p);
- ip2 = retried &&
- ip == ntohl(h->next.ip) &&
- p == ntohs(h->next.port)
- ? ntohl(h->next.ip2) : ip2_from;
- while (ip2 <= ip2_to) {
+ do {
e.ip2 = htonl(ip2);
- ip2_last = ip_set_range_to_cidr(ip2, ip2_to,
- &cidr);
+ ip2 = ip_set_range_to_cidr(ip2, ip2_to, &cidr);
e.cidr = cidr - 1;
ret = adtfn(set, &e, &ext, &ext, flags);
@@ -292,9 +291,10 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
return ret;
ret = 0;
- ip2 = ip2_last + 1;
- }
+ } while (ip2++ < ip2_to);
+ ip2 = ip2_from;
}
+ p = port;
}
return ret;
}
diff --git a/net/netfilter/ipset/ip_set_hash_net.c b/net/netfilter/ipset/ip_set_hash_net.c
index 1c67a1761e45..5449e23af13a 100644
--- a/net/netfilter/ipset/ip_set_hash_net.c
+++ b/net/netfilter/ipset/ip_set_hash_net.c
@@ -143,7 +143,7 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *tb[],
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_net4_elem e = { .cidr = HOST_MASK };
struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
- u32 ip = 0, ip_to = 0, last;
+ u32 ip = 0, ip_to = 0;
int ret;
if (tb[IPSET_ATTR_LINENO])
@@ -193,16 +193,15 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *tb[],
}
if (retried)
ip = ntohl(h->next.ip);
- while (ip <= ip_to) {
+ do {
e.ip = htonl(ip);
- last = ip_set_range_to_cidr(ip, ip_to, &e.cidr);
+ ip = ip_set_range_to_cidr(ip, ip_to, &e.cidr);
ret = adtfn(set, &e, &ext, &ext, flags);
if (ret && !ip_set_eexist(ret, flags))
return ret;
ret = 0;
- ip = last + 1;
- }
+ } while (ip++ < ip_to);
return ret;
}
diff --git a/net/netfilter/ipset/ip_set_hash_netiface.c b/net/netfilter/ipset/ip_set_hash_netiface.c
index d417074f1c1a..f5164c1efce2 100644
--- a/net/netfilter/ipset/ip_set_hash_netiface.c
+++ b/net/netfilter/ipset/ip_set_hash_netiface.c
@@ -200,7 +200,7 @@ hash_netiface4_uadt(struct ip_set *set, struct nlattr *tb[],
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netiface4_elem e = { .cidr = HOST_MASK, .elem = 1 };
struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
- u32 ip = 0, ip_to = 0, last;
+ u32 ip = 0, ip_to = 0;
int ret;
if (tb[IPSET_ATTR_LINENO])
@@ -255,17 +255,16 @@ hash_netiface4_uadt(struct ip_set *set, struct nlattr *tb[],
if (retried)
ip = ntohl(h->next.ip);
- while (ip <= ip_to) {
+ do {
e.ip = htonl(ip);
- last = ip_set_range_to_cidr(ip, ip_to, &e.cidr);
+ ip = ip_set_range_to_cidr(ip, ip_to, &e.cidr);
ret = adtfn(set, &e, &ext, &ext, flags);
if (ret && !ip_set_eexist(ret, flags))
return ret;
ret = 0;
- ip = last + 1;
- }
+ } while (ip++ < ip_to);
return ret;
}
diff --git a/net/netfilter/ipset/ip_set_hash_netnet.c b/net/netfilter/ipset/ip_set_hash_netnet.c
index 7f9ae2e9645b..5a2b923bd81f 100644
--- a/net/netfilter/ipset/ip_set_hash_netnet.c
+++ b/net/netfilter/ipset/ip_set_hash_netnet.c
@@ -169,8 +169,8 @@ hash_netnet4_uadt(struct ip_set *set, struct nlattr *tb[],
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netnet4_elem e = { };
struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
- u32 ip = 0, ip_to = 0, last;
- u32 ip2 = 0, ip2_from = 0, ip2_to = 0, last2;
+ u32 ip = 0, ip_to = 0;
+ u32 ip2 = 0, ip2_from = 0, ip2_to = 0;
int ret;
if (tb[IPSET_ATTR_LINENO])
@@ -247,27 +247,27 @@ hash_netnet4_uadt(struct ip_set *set, struct nlattr *tb[],
ip_set_mask_from_to(ip2_from, ip2_to, e.cidr[1]);
}
- if (retried)
+ if (retried) {
ip = ntohl(h->next.ip[0]);
+ ip2 = ntohl(h->next.ip[1]);
+ } else {
+ ip2 = ip2_from;
+ }
- while (ip <= ip_to) {
+ do {
e.ip[0] = htonl(ip);
- last = ip_set_range_to_cidr(ip, ip_to, &e.cidr[0]);
- ip2 = (retried &&
- ip == ntohl(h->next.ip[0])) ? ntohl(h->next.ip[1])
- : ip2_from;
- while (ip2 <= ip2_to) {
+ ip = ip_set_range_to_cidr(ip, ip_to, &e.cidr[0]);
+ do {
e.ip[1] = htonl(ip2);
- last2 = ip_set_range_to_cidr(ip2, ip2_to, &e.cidr[1]);
+ ip2 = ip_set_range_to_cidr(ip2, ip2_to, &e.cidr[1]);
ret = adtfn(set, &e, &ext, &ext, flags);
if (ret && !ip_set_eexist(ret, flags))
return ret;
ret = 0;
- ip2 = last2 + 1;
- }
- ip = last + 1;
- }
+ } while (ip2++ < ip2_to);
+ ip2 = ip2_from;
+ } while (ip++ < ip_to);
return ret;
}
diff --git a/net/netfilter/ipset/ip_set_hash_netport.c b/net/netfilter/ipset/ip_set_hash_netport.c
index e6ef382febe4..1a187be9ebc8 100644
--- a/net/netfilter/ipset/ip_set_hash_netport.c
+++ b/net/netfilter/ipset/ip_set_hash_netport.c
@@ -161,7 +161,7 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[],
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netport4_elem e = { .cidr = HOST_MASK - 1 };
struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
- u32 port, port_to, p = 0, ip = 0, ip_to = 0, last;
+ u32 port, port_to, p = 0, ip = 0, ip_to = 0;
bool with_ports = false;
u8 cidr;
int ret;
@@ -239,25 +239,26 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[],
ip_set_mask_from_to(ip, ip_to, e.cidr + 1);
}
- if (retried)
+ if (retried) {
ip = ntohl(h->next.ip);
- while (ip <= ip_to) {
+ p = ntohs(h->next.port);
+ } else {
+ p = port;
+ }
+ do {
e.ip = htonl(ip);
- last = ip_set_range_to_cidr(ip, ip_to, &cidr);
+ ip = ip_set_range_to_cidr(ip, ip_to, &cidr);
e.cidr = cidr - 1;
- p = retried && ip == ntohl(h->next.ip) ? ntohs(h->next.port)
- : port;
for (; p <= port_to; p++) {
e.port = htons(p);
ret = adtfn(set, &e, &ext, &ext, flags);
-
if (ret && !ip_set_eexist(ret, flags))
return ret;
ret = 0;
}
- ip = last + 1;
- }
+ p = port;
+ } while (ip++ < ip_to);
return ret;
}
diff --git a/net/netfilter/ipset/ip_set_hash_netportnet.c b/net/netfilter/ipset/ip_set_hash_netportnet.c
index 8602f2595a1a..d391485a6acd 100644
--- a/net/netfilter/ipset/ip_set_hash_netportnet.c
+++ b/net/netfilter/ipset/ip_set_hash_netportnet.c
@@ -184,8 +184,8 @@ hash_netportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netportnet4_elem e = { };
struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
- u32 ip = 0, ip_to = 0, ip_last, p = 0, port, port_to;
- u32 ip2_from = 0, ip2_to = 0, ip2_last, ip2;
+ u32 ip = 0, ip_to = 0, p = 0, port, port_to;
+ u32 ip2_from = 0, ip2_to = 0, ip2;
bool with_ports = false;
int ret;
@@ -288,33 +288,34 @@ hash_netportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
ip_set_mask_from_to(ip2_from, ip2_to, e.cidr[1]);
}
- if (retried)
+ if (retried) {
ip = ntohl(h->next.ip[0]);
+ p = ntohs(h->next.port);
+ ip2 = ntohl(h->next.ip[1]);
+ } else {
+ p = port;
+ ip2 = ip2_from;
+ }
- while (ip <= ip_to) {
+ do {
e.ip[0] = htonl(ip);
- ip_last = ip_set_range_to_cidr(ip, ip_to, &e.cidr[0]);
- p = retried && ip == ntohl(h->next.ip[0]) ? ntohs(h->next.port)
- : port;
+ ip = ip_set_range_to_cidr(ip, ip_to, &e.cidr[0]);
for (; p <= port_to; p++) {
e.port = htons(p);
- ip2 = (retried && ip == ntohl(h->next.ip[0]) &&
- p == ntohs(h->next.port)) ? ntohl(h->next.ip[1])
- : ip2_from;
- while (ip2 <= ip2_to) {
+ do {
e.ip[1] = htonl(ip2);
- ip2_last = ip_set_range_to_cidr(ip2, ip2_to,
- &e.cidr[1]);
+ ip2 = ip_set_range_to_cidr(ip2, ip2_to,
+ &e.cidr[1]);
ret = adtfn(set, &e, &ext, &ext, flags);
if (ret && !ip_set_eexist(ret, flags))
return ret;
ret = 0;
- ip2 = ip2_last + 1;
- }
+ } while (ip2++ < ip2_to);
+ ip2 = ip2_from;
}
- ip = ip_last + 1;
- }
+ p = port;
+ } while (ip++ < ip_to);
return ret;
}
diff --git a/net/netfilter/nf_flow_table.c b/net/netfilter/nf_flow_table.c
index 2f5099cb85b8..ec410cae9307 100644
--- a/net/netfilter/nf_flow_table.c
+++ b/net/netfilter/nf_flow_table.c
@@ -4,6 +4,7 @@
#include <linux/netfilter.h>
#include <linux/rhashtable.h>
#include <linux/netdevice.h>
+#include <net/netfilter/nf_tables.h>
#include <net/netfilter/nf_flow_table.h>
#include <net/netfilter/nf_conntrack.h>
#include <net/netfilter/nf_conntrack_core.h>
@@ -124,7 +125,9 @@ void flow_offload_free(struct flow_offload *flow)
dst_release(flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.dst_cache);
dst_release(flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.dst_cache);
e = container_of(flow, struct flow_offload_entry, flow);
- kfree(e);
+ nf_ct_delete(e->ct, 0, 0);
+ nf_ct_put(e->ct);
+ kfree_rcu(e, rcu_head);
}
EXPORT_SYMBOL_GPL(flow_offload_free);
@@ -148,11 +151,9 @@ int flow_offload_add(struct nf_flowtable *flow_table, struct flow_offload *flow)
}
EXPORT_SYMBOL_GPL(flow_offload_add);
-void flow_offload_del(struct nf_flowtable *flow_table,
- struct flow_offload *flow)
+static void flow_offload_del(struct nf_flowtable *flow_table,
+ struct flow_offload *flow)
{
- struct flow_offload_entry *e;
-
rhashtable_remove_fast(&flow_table->rhashtable,
&flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].node,
*flow_table->type->params);
@@ -160,10 +161,8 @@ void flow_offload_del(struct nf_flowtable *flow_table,
&flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].node,
*flow_table->type->params);
- e = container_of(flow, struct flow_offload_entry, flow);
- kfree_rcu(e, rcu_head);
+ flow_offload_free(flow);
}
-EXPORT_SYMBOL_GPL(flow_offload_del);
struct flow_offload_tuple_rhash *
flow_offload_lookup(struct nf_flowtable *flow_table,
@@ -174,15 +173,6 @@ flow_offload_lookup(struct nf_flowtable *flow_table,
}
EXPORT_SYMBOL_GPL(flow_offload_lookup);
-static void nf_flow_release_ct(const struct flow_offload *flow)
-{
- struct flow_offload_entry *e;
-
- e = container_of(flow, struct flow_offload_entry, flow);
- nf_ct_delete(e->ct, 0, 0);
- nf_ct_put(e->ct);
-}
-
int nf_flow_table_iterate(struct nf_flowtable *flow_table,
void (*iter)(struct flow_offload *flow, void *data),
void *data)
@@ -231,19 +221,16 @@ static inline bool nf_flow_is_dying(const struct flow_offload *flow)
return flow->flags & FLOW_OFFLOAD_DYING;
}
-void nf_flow_offload_work_gc(struct work_struct *work)
+static int nf_flow_offload_gc_step(struct nf_flowtable *flow_table)
{
struct flow_offload_tuple_rhash *tuplehash;
- struct nf_flowtable *flow_table;
struct rhashtable_iter hti;
struct flow_offload *flow;
int err;
- flow_table = container_of(work, struct nf_flowtable, gc_work.work);
-
err = rhashtable_walk_init(&flow_table->rhashtable, &hti, GFP_KERNEL);
if (err)
- goto schedule;
+ return 0;
rhashtable_walk_start(&hti);
@@ -261,15 +248,22 @@ void nf_flow_offload_work_gc(struct work_struct *work)
flow = container_of(tuplehash, struct flow_offload, tuplehash[0]);
if (nf_flow_has_expired(flow) ||
- nf_flow_is_dying(flow)) {
+ nf_flow_is_dying(flow))
flow_offload_del(flow_table, flow);
- nf_flow_release_ct(flow);
- }
}
out:
rhashtable_walk_stop(&hti);
rhashtable_walk_exit(&hti);
-schedule:
+
+ return 1;
+}
+
+void nf_flow_offload_work_gc(struct work_struct *work)
+{
+ struct nf_flowtable *flow_table;
+
+ flow_table = container_of(work, struct nf_flowtable, gc_work.work);
+ nf_flow_offload_gc_step(flow_table);
queue_delayed_work(system_power_efficient_wq, &flow_table->gc_work, HZ);
}
EXPORT_SYMBOL_GPL(nf_flow_offload_work_gc);
@@ -425,5 +419,35 @@ int nf_flow_dnat_port(const struct flow_offload *flow,
}
EXPORT_SYMBOL_GPL(nf_flow_dnat_port);
+static void nf_flow_table_do_cleanup(struct flow_offload *flow, void *data)
+{
+ struct net_device *dev = data;
+
+ if (dev && flow->tuplehash[0].tuple.iifidx != dev->ifindex)
+ return;
+
+ flow_offload_dead(flow);
+}
+
+static void nf_flow_table_iterate_cleanup(struct nf_flowtable *flowtable,
+ void *data)
+{
+ nf_flow_table_iterate(flowtable, nf_flow_table_do_cleanup, data);
+ flush_delayed_work(&flowtable->gc_work);
+}
+
+void nf_flow_table_cleanup(struct net *net, struct net_device *dev)
+{
+ nft_flow_table_iterate(net, nf_flow_table_iterate_cleanup, dev);
+}
+EXPORT_SYMBOL_GPL(nf_flow_table_cleanup);
+
+void nf_flow_table_free(struct nf_flowtable *flow_table)
+{
+ nf_flow_table_iterate(flow_table, nf_flow_table_do_cleanup, NULL);
+ WARN_ON(!nf_flow_offload_gc_step(flow_table));
+}
+EXPORT_SYMBOL_GPL(nf_flow_table_free);
+
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>");
diff --git a/net/netfilter/nf_flow_table_inet.c b/net/netfilter/nf_flow_table_inet.c
index 281209aeba8f..375a1881d93d 100644
--- a/net/netfilter/nf_flow_table_inet.c
+++ b/net/netfilter/nf_flow_table_inet.c
@@ -24,6 +24,7 @@ static struct nf_flowtable_type flowtable_inet = {
.family = NFPROTO_INET,
.params = &nf_flow_offload_rhash_params,
.gc = nf_flow_offload_work_gc,
+ .free = nf_flow_table_free,
.hook = nf_flow_offload_inet_hook,
.owner = THIS_MODULE,
};
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index 0791813a1e7d..8b9fe30de0cd 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -5006,13 +5006,13 @@ void nft_flow_table_iterate(struct net *net,
struct nft_flowtable *flowtable;
const struct nft_table *table;
- rcu_read_lock();
- list_for_each_entry_rcu(table, &net->nft.tables, list) {
- list_for_each_entry_rcu(flowtable, &table->flowtables, list) {
+ nfnl_lock(NFNL_SUBSYS_NFTABLES);
+ list_for_each_entry(table, &net->nft.tables, list) {
+ list_for_each_entry(flowtable, &table->flowtables, list) {
iter(&flowtable->data, data);
}
}
- rcu_read_unlock();
+ nfnl_unlock(NFNL_SUBSYS_NFTABLES);
}
EXPORT_SYMBOL_GPL(nft_flow_table_iterate);
@@ -5399,17 +5399,12 @@ err:
nfnetlink_set_err(ctx->net, ctx->portid, NFNLGRP_NFTABLES, -ENOBUFS);
}
-static void nft_flowtable_destroy(void *ptr, void *arg)
-{
- kfree(ptr);
-}
-
static void nf_tables_flowtable_destroy(struct nft_flowtable *flowtable)
{
cancel_delayed_work_sync(&flowtable->data.gc_work);
kfree(flowtable->name);
- rhashtable_free_and_destroy(&flowtable->data.rhashtable,
- nft_flowtable_destroy, NULL);
+ flowtable->data.type->free(&flowtable->data);
+ rhashtable_destroy(&flowtable->data.rhashtable);
module_put(flowtable->data.type->owner);
}
diff --git a/net/netfilter/nft_flow_offload.c b/net/netfilter/nft_flow_offload.c
index 4503b8dcf9c0..b65829b2be22 100644
--- a/net/netfilter/nft_flow_offload.c
+++ b/net/netfilter/nft_flow_offload.c
@@ -194,22 +194,6 @@ static struct nft_expr_type nft_flow_offload_type __read_mostly = {
.owner = THIS_MODULE,
};
-static void flow_offload_iterate_cleanup(struct flow_offload *flow, void *data)
-{
- struct net_device *dev = data;
-
- if (dev && flow->tuplehash[0].tuple.iifidx != dev->ifindex)
- return;
-
- flow_offload_dead(flow);
-}
-
-static void nft_flow_offload_iterate_cleanup(struct nf_flowtable *flowtable,
- void *data)
-{
- nf_flow_table_iterate(flowtable, flow_offload_iterate_cleanup, data);
-}
-
static int flow_offload_netdev_event(struct notifier_block *this,
unsigned long event, void *ptr)
{
@@ -218,7 +202,7 @@ static int flow_offload_netdev_event(struct notifier_block *this,
if (event != NETDEV_DOWN)
return NOTIFY_DONE;
- nft_flow_table_iterate(dev_net(dev), nft_flow_offload_iterate_cleanup, dev);
+ nf_flow_table_cleanup(dev_net(dev), dev);
return NOTIFY_DONE;
}
@@ -246,14 +230,8 @@ register_expr:
static void __exit nft_flow_offload_module_exit(void)
{
- struct net *net;
-
nft_unregister_expr(&nft_flow_offload_type);
unregister_netdevice_notifier(&flow_offload_netdev_notifier);
- rtnl_lock();
- for_each_net(net)
- nft_flow_table_iterate(net, nft_flow_offload_iterate_cleanup, NULL);
- rtnl_unlock();
}
module_init(nft_flow_offload_module_init);
diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c
index 0b56bf05c169..2f685ee1f9c8 100644
--- a/net/netfilter/x_tables.c
+++ b/net/netfilter/x_tables.c
@@ -39,7 +39,6 @@ MODULE_LICENSE("GPL");
MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
MODULE_DESCRIPTION("{ip,ip6,arp,eb}_tables backend module");
-#define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1))
#define XT_PCPU_BLOCK_SIZE 4096
struct compat_delta {
@@ -210,6 +209,9 @@ xt_request_find_match(uint8_t nfproto, const char *name, uint8_t revision)
{
struct xt_match *match;
+ if (strnlen(name, XT_EXTENSION_MAXNAMELEN) == XT_EXTENSION_MAXNAMELEN)
+ return ERR_PTR(-EINVAL);
+
match = xt_find_match(nfproto, name, revision);
if (IS_ERR(match)) {
request_module("%st_%s", xt_prefix[nfproto], name);
@@ -252,6 +254,9 @@ struct xt_target *xt_request_find_target(u8 af, const char *name, u8 revision)
{
struct xt_target *target;
+ if (strnlen(name, XT_EXTENSION_MAXNAMELEN) == XT_EXTENSION_MAXNAMELEN)
+ return ERR_PTR(-EINVAL);
+
target = xt_find_target(af, name, revision);
if (IS_ERR(target)) {
request_module("%st_%s", xt_prefix[af], name);
@@ -1000,10 +1005,15 @@ struct xt_table_info *xt_alloc_table_info(unsigned int size)
return NULL;
/* Pedantry: prevent them from hitting BUG() in vmalloc.c --RR */
- if ((SMP_ALIGN(size) >> PAGE_SHIFT) + 2 > totalram_pages)
+ if ((size >> PAGE_SHIFT) + 2 > totalram_pages)
return NULL;
- info = kvmalloc(sz, GFP_KERNEL);
+ /* __GFP_NORETRY is not fully supported by kvmalloc but it should
+ * work reasonably well if sz is too large and bail out rather
+ * than shoot all processes down before realizing there is nothing
+ * more to reclaim.
+ */
+ info = kvmalloc(sz, GFP_KERNEL | __GFP_NORETRY);
if (!info)
return NULL;
diff --git a/net/netfilter/xt_IDLETIMER.c b/net/netfilter/xt_IDLETIMER.c
index ee3421ad108d..6c2482b709b1 100644
--- a/net/netfilter/xt_IDLETIMER.c
+++ b/net/netfilter/xt_IDLETIMER.c
@@ -252,6 +252,7 @@ static struct xt_target idletimer_tg __read_mostly = {
.family = NFPROTO_UNSPEC,
.target = idletimer_tg_target,
.targetsize = sizeof(struct idletimer_tg_info),
+ .usersize = offsetof(struct idletimer_tg_info, timer),
.checkentry = idletimer_tg_checkentry,
.destroy = idletimer_tg_destroy,
.me = THIS_MODULE,
diff --git a/net/netfilter/xt_LED.c b/net/netfilter/xt_LED.c
index 0971634e5444..1dcad893df78 100644
--- a/net/netfilter/xt_LED.c
+++ b/net/netfilter/xt_LED.c
@@ -198,6 +198,7 @@ static struct xt_target led_tg_reg __read_mostly = {
.family = NFPROTO_UNSPEC,
.target = led_tg,
.targetsize = sizeof(struct xt_led_info),
+ .usersize = offsetof(struct xt_led_info, internal_data),
.checkentry = led_tg_check,
.destroy = led_tg_destroy,
.me = THIS_MODULE,
diff --git a/net/netfilter/xt_RATEEST.c b/net/netfilter/xt_RATEEST.c
index 498b54fd04d7..141c295191f6 100644
--- a/net/netfilter/xt_RATEEST.c
+++ b/net/netfilter/xt_RATEEST.c
@@ -39,23 +39,31 @@ static void xt_rateest_hash_insert(struct xt_rateest *est)
hlist_add_head(&est->list, &rateest_hash[h]);
}
-struct xt_rateest *xt_rateest_lookup(const char *name)
+static struct xt_rateest *__xt_rateest_lookup(const char *name)
{
struct xt_rateest *est;
unsigned int h;
h = xt_rateest_hash(name);
- mutex_lock(&xt_rateest_mutex);
hlist_for_each_entry(est, &rateest_hash[h], list) {
if (strcmp(est->name, name) == 0) {
est->refcnt++;
- mutex_unlock(&xt_rateest_mutex);
return est;
}
}
- mutex_unlock(&xt_rateest_mutex);
+
return NULL;
}
+
+struct xt_rateest *xt_rateest_lookup(const char *name)
+{
+ struct xt_rateest *est;
+
+ mutex_lock(&xt_rateest_mutex);
+ est = __xt_rateest_lookup(name);
+ mutex_unlock(&xt_rateest_mutex);
+ return est;
+}
EXPORT_SYMBOL_GPL(xt_rateest_lookup);
void xt_rateest_put(struct xt_rateest *est)
@@ -100,8 +108,10 @@ static int xt_rateest_tg_checkentry(const struct xt_tgchk_param *par)
net_get_random_once(&jhash_rnd, sizeof(jhash_rnd));
- est = xt_rateest_lookup(info->name);
+ mutex_lock(&xt_rateest_mutex);
+ est = __xt_rateest_lookup(info->name);
if (est) {
+ mutex_unlock(&xt_rateest_mutex);
/*
* If estimator parameters are specified, they must match the
* existing estimator.
@@ -139,11 +149,13 @@ static int xt_rateest_tg_checkentry(const struct xt_tgchk_param *par)
info->est = est;
xt_rateest_hash_insert(est);
+ mutex_unlock(&xt_rateest_mutex);
return 0;
err2:
kfree(est);
err1:
+ mutex_unlock(&xt_rateest_mutex);
return ret;
}
diff --git a/net/netfilter/xt_cgroup.c b/net/netfilter/xt_cgroup.c
index 1db1ce59079f..891f4e7e8ea7 100644
--- a/net/netfilter/xt_cgroup.c
+++ b/net/netfilter/xt_cgroup.c
@@ -52,6 +52,7 @@ static int cgroup_mt_check_v1(const struct xt_mtchk_param *par)
return -EINVAL;
}
+ info->priv = NULL;
if (info->has_path) {
cgrp = cgroup_get_from_path(info->path);
if (IS_ERR(cgrp)) {
diff --git a/net/netfilter/xt_limit.c b/net/netfilter/xt_limit.c
index d27b5f1ea619..61403b77361c 100644
--- a/net/netfilter/xt_limit.c
+++ b/net/netfilter/xt_limit.c
@@ -193,9 +193,8 @@ static struct xt_match limit_mt_reg __read_mostly = {
.compatsize = sizeof(struct compat_xt_rateinfo),
.compat_from_user = limit_mt_compat_from_user,
.compat_to_user = limit_mt_compat_to_user,
-#else
- .usersize = offsetof(struct xt_rateinfo, prev),
#endif
+ .usersize = offsetof(struct xt_rateinfo, prev),
.me = THIS_MODULE,
};
diff --git a/net/netfilter/xt_nfacct.c b/net/netfilter/xt_nfacct.c
index cc0518fe598e..6f92d25590a8 100644
--- a/net/netfilter/xt_nfacct.c
+++ b/net/netfilter/xt_nfacct.c
@@ -62,6 +62,7 @@ static struct xt_match nfacct_mt_reg __read_mostly = {
.match = nfacct_mt,
.destroy = nfacct_mt_destroy,
.matchsize = sizeof(struct xt_nfacct_match_info),
+ .usersize = offsetof(struct xt_nfacct_match_info, nfacct),
.me = THIS_MODULE,
};
diff --git a/net/netfilter/xt_statistic.c b/net/netfilter/xt_statistic.c
index 11de55e7a868..8710fdba2ae2 100644
--- a/net/netfilter/xt_statistic.c
+++ b/net/netfilter/xt_statistic.c
@@ -84,6 +84,7 @@ static struct xt_match xt_statistic_mt_reg __read_mostly = {
.checkentry = statistic_mt_check,
.destroy = statistic_mt_destroy,
.matchsize = sizeof(struct xt_statistic_info),
+ .usersize = offsetof(struct xt_statistic_info, master),
.me = THIS_MODULE,
};
diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c
index d444daf1ac04..6f02499ef007 100644
--- a/net/netlink/genetlink.c
+++ b/net/netlink/genetlink.c
@@ -1081,6 +1081,7 @@ static int genlmsg_mcast(struct sk_buff *skb, u32 portid, unsigned long group,
{
struct sk_buff *tmp;
struct net *net, *prev = NULL;
+ bool delivered = false;
int err;
for_each_net_rcu(net) {
@@ -1092,14 +1093,21 @@ static int genlmsg_mcast(struct sk_buff *skb, u32 portid, unsigned long group,
}
err = nlmsg_multicast(prev->genl_sock, tmp,
portid, group, flags);
- if (err)
+ if (!err)
+ delivered = true;
+ else if (err != -ESRCH)
goto error;
}
prev = net;
}
- return nlmsg_multicast(prev->genl_sock, skb, portid, group, flags);
+ err = nlmsg_multicast(prev->genl_sock, skb, portid, group, flags);
+ if (!err)
+ delivered = true;
+ else if (err != -ESRCH)
+ goto error;
+ return delivered ? 0 : -ESRCH;
error:
kfree_skb(skb);
return err;
diff --git a/net/openvswitch/conntrack.c b/net/openvswitch/conntrack.c
index 62f36cc938ca..c5904f629091 100644
--- a/net/openvswitch/conntrack.c
+++ b/net/openvswitch/conntrack.c
@@ -1098,6 +1098,36 @@ static int ovs_ct_commit(struct net *net, struct sw_flow_key *key,
return 0;
}
+/* Trim the skb to the length specified by the IP/IPv6 header,
+ * removing any trailing lower-layer padding. This prepares the skb
+ * for higher-layer processing that assumes skb->len excludes padding
+ * (such as nf_ip_checksum). The caller needs to pull the skb to the
+ * network header, and ensure ip_hdr/ipv6_hdr points to valid data.
+ */
+static int ovs_skb_network_trim(struct sk_buff *skb)
+{
+ unsigned int len;
+ int err;
+
+ switch (skb->protocol) {
+ case htons(ETH_P_IP):
+ len = ntohs(ip_hdr(skb)->tot_len);
+ break;
+ case htons(ETH_P_IPV6):
+ len = sizeof(struct ipv6hdr)
+ + ntohs(ipv6_hdr(skb)->payload_len);
+ break;
+ default:
+ len = skb->len;
+ }
+
+ err = pskb_trim_rcsum(skb, len);
+ if (err)
+ kfree_skb(skb);
+
+ return err;
+}
+
/* Returns 0 on success, -EINPROGRESS if 'skb' is stolen, or other nonzero
* value if 'skb' is freed.
*/
@@ -1112,6 +1142,10 @@ int ovs_ct_execute(struct net *net, struct sk_buff *skb,
nh_ofs = skb_network_offset(skb);
skb_pull_rcsum(skb, nh_ofs);
+ err = ovs_skb_network_trim(skb);
+ if (err)
+ return err;
+
if (key->ip.frag != OVS_FRAG_TYPE_NONE) {
err = handle_fragments(net, key, info->zone.id, skb);
if (err)
diff --git a/net/rds/cong.c b/net/rds/cong.c
index 8d19fd25dce3..63da9d2f142d 100644
--- a/net/rds/cong.c
+++ b/net/rds/cong.c
@@ -223,7 +223,7 @@ void rds_cong_queue_updates(struct rds_cong_map *map)
rcu_read_lock();
if (!test_and_set_bit(0, &conn->c_map_queued) &&
- !test_bit(RDS_DESTROY_PENDING, &cp->cp_flags)) {
+ !rds_destroy_pending(cp->cp_conn)) {
rds_stats_inc(s_cong_update_queued);
/* We cannot inline the call to rds_send_xmit() here
* for two reasons (both pertaining to a TCP transport):
diff --git a/net/rds/connection.c b/net/rds/connection.c
index b10c0ef36d8d..94e190febfdd 100644
--- a/net/rds/connection.c
+++ b/net/rds/connection.c
@@ -220,8 +220,13 @@ static struct rds_connection *__rds_conn_create(struct net *net,
is_outgoing);
conn->c_path[i].cp_index = i;
}
- ret = trans->conn_alloc(conn, gfp);
+ rcu_read_lock();
+ if (rds_destroy_pending(conn))
+ ret = -ENETDOWN;
+ else
+ ret = trans->conn_alloc(conn, gfp);
if (ret) {
+ rcu_read_unlock();
kfree(conn->c_path);
kmem_cache_free(rds_conn_slab, conn);
conn = ERR_PTR(ret);
@@ -283,6 +288,7 @@ static struct rds_connection *__rds_conn_create(struct net *net,
}
}
spin_unlock_irqrestore(&rds_conn_lock, flags);
+ rcu_read_unlock();
out:
return conn;
@@ -382,13 +388,10 @@ static void rds_conn_path_destroy(struct rds_conn_path *cp)
{
struct rds_message *rm, *rtmp;
- set_bit(RDS_DESTROY_PENDING, &cp->cp_flags);
-
if (!cp->cp_transport_data)
return;
/* make sure lingering queued work won't try to ref the conn */
- synchronize_rcu();
cancel_delayed_work_sync(&cp->cp_send_w);
cancel_delayed_work_sync(&cp->cp_recv_w);
@@ -691,7 +694,7 @@ void rds_conn_path_drop(struct rds_conn_path *cp, bool destroy)
atomic_set(&cp->cp_state, RDS_CONN_ERROR);
rcu_read_lock();
- if (!destroy && test_bit(RDS_DESTROY_PENDING, &cp->cp_flags)) {
+ if (!destroy && rds_destroy_pending(cp->cp_conn)) {
rcu_read_unlock();
return;
}
@@ -714,7 +717,7 @@ EXPORT_SYMBOL_GPL(rds_conn_drop);
void rds_conn_path_connect_if_down(struct rds_conn_path *cp)
{
rcu_read_lock();
- if (test_bit(RDS_DESTROY_PENDING, &cp->cp_flags)) {
+ if (rds_destroy_pending(cp->cp_conn)) {
rcu_read_unlock();
return;
}
diff --git a/net/rds/ib.c b/net/rds/ib.c
index b2a5067b4afe..50a88f3e7e39 100644
--- a/net/rds/ib.c
+++ b/net/rds/ib.c
@@ -48,6 +48,7 @@
static unsigned int rds_ib_mr_1m_pool_size = RDS_MR_1M_POOL_SIZE;
static unsigned int rds_ib_mr_8k_pool_size = RDS_MR_8K_POOL_SIZE;
unsigned int rds_ib_retry_count = RDS_IB_DEFAULT_RETRY_COUNT;
+static atomic_t rds_ib_unloading;
module_param(rds_ib_mr_1m_pool_size, int, 0444);
MODULE_PARM_DESC(rds_ib_mr_1m_pool_size, " Max number of 1M mr per HCA");
@@ -345,7 +346,8 @@ static int rds_ib_laddr_check(struct net *net, __be32 addr)
/* Create a CMA ID and try to bind it. This catches both
* IB and iWARP capable NICs.
*/
- cm_id = rdma_create_id(&init_net, NULL, NULL, RDMA_PS_TCP, IB_QPT_RC);
+ cm_id = rdma_create_id(&init_net, rds_rdma_cm_event_handler,
+ NULL, RDMA_PS_TCP, IB_QPT_RC);
if (IS_ERR(cm_id))
return PTR_ERR(cm_id);
@@ -377,8 +379,23 @@ static void rds_ib_unregister_client(void)
flush_workqueue(rds_wq);
}
+static void rds_ib_set_unloading(void)
+{
+ atomic_set(&rds_ib_unloading, 1);
+}
+
+static bool rds_ib_is_unloading(struct rds_connection *conn)
+{
+ struct rds_conn_path *cp = &conn->c_path[0];
+
+ return (test_bit(RDS_DESTROY_PENDING, &cp->cp_flags) ||
+ atomic_read(&rds_ib_unloading) != 0);
+}
+
void rds_ib_exit(void)
{
+ rds_ib_set_unloading();
+ synchronize_rcu();
rds_info_deregister_func(RDS_INFO_IB_CONNECTIONS, rds_ib_ic_info);
rds_ib_unregister_client();
rds_ib_destroy_nodev_conns();
@@ -412,6 +429,7 @@ struct rds_transport rds_ib_transport = {
.flush_mrs = rds_ib_flush_mrs,
.t_owner = THIS_MODULE,
.t_name = "infiniband",
+ .t_unloading = rds_ib_is_unloading,
.t_type = RDS_TRANS_IB
};
diff --git a/net/rds/ib_cm.c b/net/rds/ib_cm.c
index 80fb6f63e768..eea1d8611b20 100644
--- a/net/rds/ib_cm.c
+++ b/net/rds/ib_cm.c
@@ -117,6 +117,7 @@ void rds_ib_cm_connect_complete(struct rds_connection *conn, struct rdma_cm_even
&conn->c_laddr, &conn->c_faddr,
RDS_PROTOCOL_MAJOR(conn->c_version),
RDS_PROTOCOL_MINOR(conn->c_version));
+ set_bit(RDS_DESTROY_PENDING, &conn->c_path[0].cp_flags);
rds_conn_destroy(conn);
return;
} else {
diff --git a/net/rds/rds.h b/net/rds/rds.h
index 374ae83b60d4..7301b9b01890 100644
--- a/net/rds/rds.h
+++ b/net/rds/rds.h
@@ -518,6 +518,7 @@ struct rds_transport {
void (*sync_mr)(void *trans_private, int direction);
void (*free_mr)(void *trans_private, int invalidate);
void (*flush_mrs)(void);
+ bool (*t_unloading)(struct rds_connection *conn);
};
struct rds_sock {
@@ -862,6 +863,12 @@ static inline void rds_mr_put(struct rds_mr *mr)
__rds_put_mr_final(mr);
}
+static inline bool rds_destroy_pending(struct rds_connection *conn)
+{
+ return !check_net(rds_conn_net(conn)) ||
+ (conn->c_trans->t_unloading && conn->c_trans->t_unloading(conn));
+}
+
/* stats.c */
DECLARE_PER_CPU_SHARED_ALIGNED(struct rds_statistics, rds_stats);
#define rds_stats_inc_which(which, member) do { \
diff --git a/net/rds/send.c b/net/rds/send.c
index d3e32d1f3c7d..b1b0022b8370 100644
--- a/net/rds/send.c
+++ b/net/rds/send.c
@@ -162,7 +162,7 @@ restart:
goto out;
}
- if (test_bit(RDS_DESTROY_PENDING, &cp->cp_flags)) {
+ if (rds_destroy_pending(cp->cp_conn)) {
release_in_xmit(cp);
ret = -ENETUNREACH; /* dont requeue send work */
goto out;
@@ -444,7 +444,7 @@ over_batch:
if (batch_count < send_batch_count)
goto restart;
rcu_read_lock();
- if (test_bit(RDS_DESTROY_PENDING, &cp->cp_flags))
+ if (rds_destroy_pending(cp->cp_conn))
ret = -ENETUNREACH;
else
queue_delayed_work(rds_wq, &cp->cp_send_w, 1);
@@ -1162,7 +1162,7 @@ int rds_sendmsg(struct socket *sock, struct msghdr *msg, size_t payload_len)
else
cpath = &conn->c_path[0];
- if (test_bit(RDS_DESTROY_PENDING, &cpath->cp_flags)) {
+ if (rds_destroy_pending(conn)) {
ret = -EAGAIN;
goto out;
}
@@ -1209,7 +1209,7 @@ int rds_sendmsg(struct socket *sock, struct msghdr *msg, size_t payload_len)
if (ret == -ENOMEM || ret == -EAGAIN) {
ret = 0;
rcu_read_lock();
- if (test_bit(RDS_DESTROY_PENDING, &cpath->cp_flags))
+ if (rds_destroy_pending(cpath->cp_conn))
ret = -ENETUNREACH;
else
queue_delayed_work(rds_wq, &cpath->cp_send_w, 1);
@@ -1295,7 +1295,7 @@ rds_send_probe(struct rds_conn_path *cp, __be16 sport,
/* schedule the send work on rds_wq */
rcu_read_lock();
- if (!test_bit(RDS_DESTROY_PENDING, &cp->cp_flags))
+ if (!rds_destroy_pending(cp->cp_conn))
queue_delayed_work(rds_wq, &cp->cp_send_w, 1);
rcu_read_unlock();
diff --git a/net/rds/tcp.c b/net/rds/tcp.c
index 9920d2f84eff..44c4652721af 100644
--- a/net/rds/tcp.c
+++ b/net/rds/tcp.c
@@ -49,6 +49,7 @@ static unsigned int rds_tcp_tc_count;
/* Track rds_tcp_connection structs so they can be cleaned up */
static DEFINE_SPINLOCK(rds_tcp_conn_lock);
static LIST_HEAD(rds_tcp_conn_list);
+static atomic_t rds_tcp_unloading = ATOMIC_INIT(0);
static struct kmem_cache *rds_tcp_conn_slab;
@@ -274,14 +275,13 @@ static int rds_tcp_laddr_check(struct net *net, __be32 addr)
static void rds_tcp_conn_free(void *arg)
{
struct rds_tcp_connection *tc = arg;
- unsigned long flags;
rdsdebug("freeing tc %p\n", tc);
- spin_lock_irqsave(&rds_tcp_conn_lock, flags);
+ spin_lock_bh(&rds_tcp_conn_lock);
if (!tc->t_tcp_node_detached)
list_del(&tc->t_tcp_node);
- spin_unlock_irqrestore(&rds_tcp_conn_lock, flags);
+ spin_unlock_bh(&rds_tcp_conn_lock);
kmem_cache_free(rds_tcp_conn_slab, tc);
}
@@ -296,7 +296,7 @@ static int rds_tcp_conn_alloc(struct rds_connection *conn, gfp_t gfp)
tc = kmem_cache_alloc(rds_tcp_conn_slab, gfp);
if (!tc) {
ret = -ENOMEM;
- break;
+ goto fail;
}
mutex_init(&tc->t_conn_path_lock);
tc->t_sock = NULL;
@@ -306,14 +306,19 @@ static int rds_tcp_conn_alloc(struct rds_connection *conn, gfp_t gfp)
conn->c_path[i].cp_transport_data = tc;
tc->t_cpath = &conn->c_path[i];
+ tc->t_tcp_node_detached = true;
- spin_lock_irq(&rds_tcp_conn_lock);
- tc->t_tcp_node_detached = false;
- list_add_tail(&tc->t_tcp_node, &rds_tcp_conn_list);
- spin_unlock_irq(&rds_tcp_conn_lock);
rdsdebug("rds_conn_path [%d] tc %p\n", i,
conn->c_path[i].cp_transport_data);
}
+ spin_lock_bh(&rds_tcp_conn_lock);
+ for (i = 0; i < RDS_MPATH_WORKERS; i++) {
+ tc = conn->c_path[i].cp_transport_data;
+ tc->t_tcp_node_detached = false;
+ list_add_tail(&tc->t_tcp_node, &rds_tcp_conn_list);
+ }
+ spin_unlock_bh(&rds_tcp_conn_lock);
+fail:
if (ret) {
for (j = 0; j < i; j++)
rds_tcp_conn_free(conn->c_path[j].cp_transport_data);
@@ -332,6 +337,16 @@ static bool list_has_conn(struct list_head *list, struct rds_connection *conn)
return false;
}
+static void rds_tcp_set_unloading(void)
+{
+ atomic_set(&rds_tcp_unloading, 1);
+}
+
+static bool rds_tcp_is_unloading(struct rds_connection *conn)
+{
+ return atomic_read(&rds_tcp_unloading) != 0;
+}
+
static void rds_tcp_destroy_conns(void)
{
struct rds_tcp_connection *tc, *_tc;
@@ -370,6 +385,7 @@ struct rds_transport rds_tcp_transport = {
.t_type = RDS_TRANS_TCP,
.t_prefer_loopback = 1,
.t_mp_capable = 1,
+ .t_unloading = rds_tcp_is_unloading,
};
static unsigned int rds_tcp_netid;
@@ -513,7 +529,7 @@ static void rds_tcp_kill_sock(struct net *net)
rtn->rds_tcp_listen_sock = NULL;
rds_tcp_listen_stop(lsock, &rtn->rds_tcp_accept_w);
- spin_lock_irq(&rds_tcp_conn_lock);
+ spin_lock_bh(&rds_tcp_conn_lock);
list_for_each_entry_safe(tc, _tc, &rds_tcp_conn_list, t_tcp_node) {
struct net *c_net = read_pnet(&tc->t_cpath->cp_conn->c_net);
@@ -526,7 +542,7 @@ static void rds_tcp_kill_sock(struct net *net)
tc->t_tcp_node_detached = true;
}
}
- spin_unlock_irq(&rds_tcp_conn_lock);
+ spin_unlock_bh(&rds_tcp_conn_lock);
list_for_each_entry_safe(tc, _tc, &tmp_list, t_tcp_node)
rds_conn_destroy(tc->t_cpath->cp_conn);
}
@@ -574,7 +590,7 @@ static void rds_tcp_sysctl_reset(struct net *net)
{
struct rds_tcp_connection *tc, *_tc;
- spin_lock_irq(&rds_tcp_conn_lock);
+ spin_lock_bh(&rds_tcp_conn_lock);
list_for_each_entry_safe(tc, _tc, &rds_tcp_conn_list, t_tcp_node) {
struct net *c_net = read_pnet(&tc->t_cpath->cp_conn->c_net);
@@ -584,7 +600,7 @@ static void rds_tcp_sysctl_reset(struct net *net)
/* reconnect with new parameters */
rds_conn_path_drop(tc->t_cpath, false);
}
- spin_unlock_irq(&rds_tcp_conn_lock);
+ spin_unlock_bh(&rds_tcp_conn_lock);
}
static int rds_tcp_skbuf_handler(struct ctl_table *ctl, int write,
@@ -607,6 +623,8 @@ static int rds_tcp_skbuf_handler(struct ctl_table *ctl, int write,
static void rds_tcp_exit(void)
{
+ rds_tcp_set_unloading();
+ synchronize_rcu();
rds_info_deregister_func(RDS_INFO_TCP_SOCKETS, rds_tcp_tc_info);
unregister_pernet_subsys(&rds_tcp_net_ops);
if (unregister_netdevice_notifier(&rds_tcp_dev_notifier))
diff --git a/net/rds/tcp_connect.c b/net/rds/tcp_connect.c
index 534c67aeb20f..d999e7075645 100644
--- a/net/rds/tcp_connect.c
+++ b/net/rds/tcp_connect.c
@@ -170,7 +170,7 @@ void rds_tcp_conn_path_shutdown(struct rds_conn_path *cp)
cp->cp_conn, tc, sock);
if (sock) {
- if (test_bit(RDS_DESTROY_PENDING, &cp->cp_flags))
+ if (rds_destroy_pending(cp->cp_conn))
rds_tcp_set_linger(sock);
sock->ops->shutdown(sock, RCV_SHUTDOWN | SEND_SHUTDOWN);
lock_sock(sock->sk);
diff --git a/net/rds/tcp_recv.c b/net/rds/tcp_recv.c
index dd707b9e73e5..b9fbd2ee74ef 100644
--- a/net/rds/tcp_recv.c
+++ b/net/rds/tcp_recv.c
@@ -323,7 +323,7 @@ void rds_tcp_data_ready(struct sock *sk)
if (rds_tcp_read_sock(cp, GFP_ATOMIC) == -ENOMEM) {
rcu_read_lock();
- if (!test_bit(RDS_DESTROY_PENDING, &cp->cp_flags))
+ if (!rds_destroy_pending(cp->cp_conn))
queue_delayed_work(rds_wq, &cp->cp_recv_w, 0);
rcu_read_unlock();
}
diff --git a/net/rds/tcp_send.c b/net/rds/tcp_send.c
index 16f65744d984..7df869d37afd 100644
--- a/net/rds/tcp_send.c
+++ b/net/rds/tcp_send.c
@@ -204,7 +204,7 @@ void rds_tcp_write_space(struct sock *sk)
rcu_read_lock();
if ((refcount_read(&sk->sk_wmem_alloc) << 1) <= sk->sk_sndbuf &&
- !test_bit(RDS_DESTROY_PENDING, &cp->cp_flags))
+ !rds_destroy_pending(cp->cp_conn))
queue_delayed_work(rds_wq, &cp->cp_send_w, 0);
rcu_read_unlock();
diff --git a/net/rds/threads.c b/net/rds/threads.c
index eb76db1360b0..c52861d77a59 100644
--- a/net/rds/threads.c
+++ b/net/rds/threads.c
@@ -88,7 +88,7 @@ void rds_connect_path_complete(struct rds_conn_path *cp, int curr)
cp->cp_reconnect_jiffies = 0;
set_bit(0, &cp->cp_conn->c_map_queued);
rcu_read_lock();
- if (!test_bit(RDS_DESTROY_PENDING, &cp->cp_flags)) {
+ if (!rds_destroy_pending(cp->cp_conn)) {
queue_delayed_work(rds_wq, &cp->cp_send_w, 0);
queue_delayed_work(rds_wq, &cp->cp_recv_w, 0);
}
@@ -138,7 +138,7 @@ void rds_queue_reconnect(struct rds_conn_path *cp)
if (cp->cp_reconnect_jiffies == 0) {
cp->cp_reconnect_jiffies = rds_sysctl_reconnect_min_jiffies;
rcu_read_lock();
- if (!test_bit(RDS_DESTROY_PENDING, &cp->cp_flags))
+ if (!rds_destroy_pending(cp->cp_conn))
queue_delayed_work(rds_wq, &cp->cp_conn_w, 0);
rcu_read_unlock();
return;
@@ -149,7 +149,7 @@ void rds_queue_reconnect(struct rds_conn_path *cp)
rand % cp->cp_reconnect_jiffies, cp->cp_reconnect_jiffies,
conn, &conn->c_laddr, &conn->c_faddr);
rcu_read_lock();
- if (!test_bit(RDS_DESTROY_PENDING, &cp->cp_flags))
+ if (!rds_destroy_pending(cp->cp_conn))
queue_delayed_work(rds_wq, &cp->cp_conn_w,
rand % cp->cp_reconnect_jiffies);
rcu_read_unlock();
diff --git a/net/rxrpc/conn_client.c b/net/rxrpc/conn_client.c
index 7f74ca3059f8..064175068059 100644
--- a/net/rxrpc/conn_client.c
+++ b/net/rxrpc/conn_client.c
@@ -834,7 +834,8 @@ void rxrpc_disconnect_client_call(struct rxrpc_call *call)
* can be skipped if we find a follow-on call. The first DATA packet
* of the follow on call will implicitly ACK this call.
*/
- if (test_bit(RXRPC_CALL_EXPOSED, &call->flags)) {
+ if (call->completion == RXRPC_CALL_SUCCEEDED &&
+ test_bit(RXRPC_CALL_EXPOSED, &call->flags)) {
unsigned long final_ack_at = jiffies + 2;
WRITE_ONCE(chan->final_ack_at, final_ack_at);
diff --git a/net/rxrpc/conn_event.c b/net/rxrpc/conn_event.c
index 4ca11be6be3c..b1dfae107431 100644
--- a/net/rxrpc/conn_event.c
+++ b/net/rxrpc/conn_event.c
@@ -460,6 +460,7 @@ void rxrpc_process_connection(struct work_struct *work)
case -EKEYEXPIRED:
case -EKEYREJECTED:
goto protocol_error;
+ case -ENOMEM:
case -EAGAIN:
goto requeue_and_leave;
case -ECONNABORTED:
diff --git a/net/rxrpc/conn_object.c b/net/rxrpc/conn_object.c
index c628351eb900..ccbac190add1 100644
--- a/net/rxrpc/conn_object.c
+++ b/net/rxrpc/conn_object.c
@@ -177,13 +177,21 @@ void __rxrpc_disconnect_call(struct rxrpc_connection *conn,
* through the channel, whilst disposing of the actual call record.
*/
trace_rxrpc_disconnect_call(call);
- if (call->abort_code) {
- chan->last_abort = call->abort_code;
- chan->last_type = RXRPC_PACKET_TYPE_ABORT;
- } else {
+ switch (call->completion) {
+ case RXRPC_CALL_SUCCEEDED:
chan->last_seq = call->rx_hard_ack;
chan->last_type = RXRPC_PACKET_TYPE_ACK;
+ break;
+ case RXRPC_CALL_LOCALLY_ABORTED:
+ chan->last_abort = call->abort_code;
+ chan->last_type = RXRPC_PACKET_TYPE_ABORT;
+ break;
+ default:
+ chan->last_abort = RX_USER_ABORT;
+ chan->last_type = RXRPC_PACKET_TYPE_ABORT;
+ break;
}
+
/* Sync with rxrpc_conn_retransmit(). */
smp_wmb();
chan->last_call = chan->call_id;
diff --git a/net/rxrpc/rxkad.c b/net/rxrpc/rxkad.c
index c38b3a1de56c..77cb23c7bd0a 100644
--- a/net/rxrpc/rxkad.c
+++ b/net/rxrpc/rxkad.c
@@ -773,8 +773,7 @@ static int rxkad_respond_to_challenge(struct rxrpc_connection *conn,
{
const struct rxrpc_key_token *token;
struct rxkad_challenge challenge;
- struct rxkad_response resp
- __attribute__((aligned(8))); /* must be aligned for crypto */
+ struct rxkad_response *resp;
struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
const char *eproto;
u32 version, nonce, min_level, abort_code;
@@ -818,26 +817,29 @@ static int rxkad_respond_to_challenge(struct rxrpc_connection *conn,
token = conn->params.key->payload.data[0];
/* build the response packet */
- memset(&resp, 0, sizeof(resp));
-
- resp.version = htonl(RXKAD_VERSION);
- resp.encrypted.epoch = htonl(conn->proto.epoch);
- resp.encrypted.cid = htonl(conn->proto.cid);
- resp.encrypted.securityIndex = htonl(conn->security_ix);
- resp.encrypted.inc_nonce = htonl(nonce + 1);
- resp.encrypted.level = htonl(conn->params.security_level);
- resp.kvno = htonl(token->kad->kvno);
- resp.ticket_len = htonl(token->kad->ticket_len);
-
- resp.encrypted.call_id[0] = htonl(conn->channels[0].call_counter);
- resp.encrypted.call_id[1] = htonl(conn->channels[1].call_counter);
- resp.encrypted.call_id[2] = htonl(conn->channels[2].call_counter);
- resp.encrypted.call_id[3] = htonl(conn->channels[3].call_counter);
+ resp = kzalloc(sizeof(struct rxkad_response), GFP_NOFS);
+ if (!resp)
+ return -ENOMEM;
+
+ resp->version = htonl(RXKAD_VERSION);
+ resp->encrypted.epoch = htonl(conn->proto.epoch);
+ resp->encrypted.cid = htonl(conn->proto.cid);
+ resp->encrypted.securityIndex = htonl(conn->security_ix);
+ resp->encrypted.inc_nonce = htonl(nonce + 1);
+ resp->encrypted.level = htonl(conn->params.security_level);
+ resp->kvno = htonl(token->kad->kvno);
+ resp->ticket_len = htonl(token->kad->ticket_len);
+ resp->encrypted.call_id[0] = htonl(conn->channels[0].call_counter);
+ resp->encrypted.call_id[1] = htonl(conn->channels[1].call_counter);
+ resp->encrypted.call_id[2] = htonl(conn->channels[2].call_counter);
+ resp->encrypted.call_id[3] = htonl(conn->channels[3].call_counter);
/* calculate the response checksum and then do the encryption */
- rxkad_calc_response_checksum(&resp);
- rxkad_encrypt_response(conn, &resp, token->kad);
- return rxkad_send_response(conn, &sp->hdr, &resp, token->kad);
+ rxkad_calc_response_checksum(resp);
+ rxkad_encrypt_response(conn, resp, token->kad);
+ ret = rxkad_send_response(conn, &sp->hdr, resp, token->kad);
+ kfree(resp);
+ return ret;
protocol_error:
trace_rxrpc_rx_eproto(NULL, sp->hdr.serial, eproto);
@@ -1048,8 +1050,7 @@ static int rxkad_verify_response(struct rxrpc_connection *conn,
struct sk_buff *skb,
u32 *_abort_code)
{
- struct rxkad_response response
- __attribute__((aligned(8))); /* must be aligned for crypto */
+ struct rxkad_response *response;
struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
struct rxrpc_crypt session_key;
const char *eproto;
@@ -1061,17 +1062,22 @@ static int rxkad_verify_response(struct rxrpc_connection *conn,
_enter("{%d,%x}", conn->debug_id, key_serial(conn->server_key));
+ ret = -ENOMEM;
+ response = kzalloc(sizeof(struct rxkad_response), GFP_NOFS);
+ if (!response)
+ goto temporary_error;
+
eproto = tracepoint_string("rxkad_rsp_short");
abort_code = RXKADPACKETSHORT;
if (skb_copy_bits(skb, sizeof(struct rxrpc_wire_header),
- &response, sizeof(response)) < 0)
+ response, sizeof(*response)) < 0)
goto protocol_error;
- if (!pskb_pull(skb, sizeof(response)))
+ if (!pskb_pull(skb, sizeof(*response)))
BUG();
- version = ntohl(response.version);
- ticket_len = ntohl(response.ticket_len);
- kvno = ntohl(response.kvno);
+ version = ntohl(response->version);
+ ticket_len = ntohl(response->ticket_len);
+ kvno = ntohl(response->kvno);
_proto("Rx RESPONSE %%%u { v=%u kv=%u tl=%u }",
sp->hdr.serial, version, kvno, ticket_len);
@@ -1105,31 +1111,31 @@ static int rxkad_verify_response(struct rxrpc_connection *conn,
ret = rxkad_decrypt_ticket(conn, skb, ticket, ticket_len, &session_key,
&expiry, _abort_code);
if (ret < 0)
- goto temporary_error_free;
+ goto temporary_error_free_resp;
/* use the session key from inside the ticket to decrypt the
* response */
- rxkad_decrypt_response(conn, &response, &session_key);
+ rxkad_decrypt_response(conn, response, &session_key);
eproto = tracepoint_string("rxkad_rsp_param");
abort_code = RXKADSEALEDINCON;
- if (ntohl(response.encrypted.epoch) != conn->proto.epoch)
+ if (ntohl(response->encrypted.epoch) != conn->proto.epoch)
goto protocol_error_free;
- if (ntohl(response.encrypted.cid) != conn->proto.cid)
+ if (ntohl(response->encrypted.cid) != conn->proto.cid)
goto protocol_error_free;
- if (ntohl(response.encrypted.securityIndex) != conn->security_ix)
+ if (ntohl(response->encrypted.securityIndex) != conn->security_ix)
goto protocol_error_free;
- csum = response.encrypted.checksum;
- response.encrypted.checksum = 0;
- rxkad_calc_response_checksum(&response);
+ csum = response->encrypted.checksum;
+ response->encrypted.checksum = 0;
+ rxkad_calc_response_checksum(response);
eproto = tracepoint_string("rxkad_rsp_csum");
- if (response.encrypted.checksum != csum)
+ if (response->encrypted.checksum != csum)
goto protocol_error_free;
spin_lock(&conn->channel_lock);
for (i = 0; i < RXRPC_MAXCALLS; i++) {
struct rxrpc_call *call;
- u32 call_id = ntohl(response.encrypted.call_id[i]);
+ u32 call_id = ntohl(response->encrypted.call_id[i]);
eproto = tracepoint_string("rxkad_rsp_callid");
if (call_id > INT_MAX)
@@ -1153,12 +1159,12 @@ static int rxkad_verify_response(struct rxrpc_connection *conn,
eproto = tracepoint_string("rxkad_rsp_seq");
abort_code = RXKADOUTOFSEQUENCE;
- if (ntohl(response.encrypted.inc_nonce) != conn->security_nonce + 1)
+ if (ntohl(response->encrypted.inc_nonce) != conn->security_nonce + 1)
goto protocol_error_free;
eproto = tracepoint_string("rxkad_rsp_level");
abort_code = RXKADLEVELFAIL;
- level = ntohl(response.encrypted.level);
+ level = ntohl(response->encrypted.level);
if (level > RXRPC_SECURITY_ENCRYPT)
goto protocol_error_free;
conn->params.security_level = level;
@@ -1168,9 +1174,10 @@ static int rxkad_verify_response(struct rxrpc_connection *conn,
* as for a client connection */
ret = rxrpc_get_server_data_key(conn, &session_key, expiry, kvno);
if (ret < 0)
- goto temporary_error_free;
+ goto temporary_error_free_ticket;
kfree(ticket);
+ kfree(response);
_leave(" = 0");
return 0;
@@ -1179,12 +1186,15 @@ protocol_error_unlock:
protocol_error_free:
kfree(ticket);
protocol_error:
+ kfree(response);
trace_rxrpc_rx_eproto(NULL, sp->hdr.serial, eproto);
*_abort_code = abort_code;
return -EPROTO;
-temporary_error_free:
+temporary_error_free_ticket:
kfree(ticket);
+temporary_error_free_resp:
+ kfree(response);
temporary_error:
/* Ignore the response packet if we got a temporary error such as
* ENOMEM. We just want to send the challenge again. Note that we
diff --git a/net/sched/act_api.c b/net/sched/act_api.c
index 52622a3d2517..eba6682727dd 100644
--- a/net/sched/act_api.c
+++ b/net/sched/act_api.c
@@ -78,7 +78,7 @@ static void free_tcf(struct tc_action *p)
static void tcf_idr_remove(struct tcf_idrinfo *idrinfo, struct tc_action *p)
{
spin_lock_bh(&idrinfo->lock);
- idr_remove_ext(&idrinfo->action_idr, p->tcfa_index);
+ idr_remove(&idrinfo->action_idr, p->tcfa_index);
spin_unlock_bh(&idrinfo->lock);
gen_kill_estimator(&p->tcfa_rate_est);
free_tcf(p);
@@ -124,7 +124,7 @@ static int tcf_dump_walker(struct tcf_idrinfo *idrinfo, struct sk_buff *skb,
s_i = cb->args[0];
- idr_for_each_entry_ext(idr, p, id) {
+ idr_for_each_entry_ul(idr, p, id) {
index++;
if (index < s_i)
continue;
@@ -181,7 +181,7 @@ static int tcf_del_walker(struct tcf_idrinfo *idrinfo, struct sk_buff *skb,
if (nla_put_string(skb, TCA_KIND, ops->kind))
goto nla_put_failure;
- idr_for_each_entry_ext(idr, p, id) {
+ idr_for_each_entry_ul(idr, p, id) {
ret = __tcf_idr_release(p, false, true);
if (ret == ACT_P_DELETED) {
module_put(ops->owner);
@@ -222,7 +222,7 @@ static struct tc_action *tcf_idr_lookup(u32 index, struct tcf_idrinfo *idrinfo)
struct tc_action *p = NULL;
spin_lock_bh(&idrinfo->lock);
- p = idr_find_ext(&idrinfo->action_idr, index);
+ p = idr_find(&idrinfo->action_idr, index);
spin_unlock_bh(&idrinfo->lock);
return p;
@@ -274,7 +274,6 @@ int tcf_idr_create(struct tc_action_net *tn, u32 index, struct nlattr *est,
struct tcf_idrinfo *idrinfo = tn->idrinfo;
struct idr *idr = &idrinfo->action_idr;
int err = -ENOMEM;
- unsigned long idr_index;
if (unlikely(!p))
return -ENOMEM;
@@ -284,45 +283,28 @@ int tcf_idr_create(struct tc_action_net *tn, u32 index, struct nlattr *est,
if (cpustats) {
p->cpu_bstats = netdev_alloc_pcpu_stats(struct gnet_stats_basic_cpu);
- if (!p->cpu_bstats) {
-err1:
- kfree(p);
- return err;
- }
- p->cpu_qstats = alloc_percpu(struct gnet_stats_queue);
- if (!p->cpu_qstats) {
-err2:
- free_percpu(p->cpu_bstats);
+ if (!p->cpu_bstats)
goto err1;
- }
+ p->cpu_qstats = alloc_percpu(struct gnet_stats_queue);
+ if (!p->cpu_qstats)
+ goto err2;
}
spin_lock_init(&p->tcfa_lock);
+ idr_preload(GFP_KERNEL);
+ spin_lock_bh(&idrinfo->lock);
/* user doesn't specify an index */
if (!index) {
- idr_preload(GFP_KERNEL);
- spin_lock_bh(&idrinfo->lock);
- err = idr_alloc_ext(idr, NULL, &idr_index, 1, 0,
- GFP_ATOMIC);
- spin_unlock_bh(&idrinfo->lock);
- idr_preload_end();
- if (err) {
-err3:
- free_percpu(p->cpu_qstats);
- goto err2;
- }
- p->tcfa_index = idr_index;
+ index = 1;
+ err = idr_alloc_u32(idr, NULL, &index, UINT_MAX, GFP_ATOMIC);
} else {
- idr_preload(GFP_KERNEL);
- spin_lock_bh(&idrinfo->lock);
- err = idr_alloc_ext(idr, NULL, NULL, index, index + 1,
- GFP_ATOMIC);
- spin_unlock_bh(&idrinfo->lock);
- idr_preload_end();
- if (err)
- goto err3;
- p->tcfa_index = index;
+ err = idr_alloc_u32(idr, NULL, &index, index, GFP_ATOMIC);
}
+ spin_unlock_bh(&idrinfo->lock);
+ idr_preload_end();
+ if (err)
+ goto err3;
+ p->tcfa_index = index;
p->tcfa_tm.install = jiffies;
p->tcfa_tm.lastuse = jiffies;
p->tcfa_tm.firstuse = 0;
@@ -330,9 +312,8 @@ err3:
err = gen_new_estimator(&p->tcfa_bstats, p->cpu_bstats,
&p->tcfa_rate_est,
&p->tcfa_lock, NULL, est);
- if (err) {
- goto err3;
- }
+ if (err)
+ goto err4;
}
p->idrinfo = idrinfo;
@@ -340,6 +321,15 @@ err3:
INIT_LIST_HEAD(&p->list);
*a = p;
return 0;
+err4:
+ idr_remove(idr, index);
+err3:
+ free_percpu(p->cpu_qstats);
+err2:
+ free_percpu(p->cpu_bstats);
+err1:
+ kfree(p);
+ return err;
}
EXPORT_SYMBOL(tcf_idr_create);
@@ -348,7 +338,7 @@ void tcf_idr_insert(struct tc_action_net *tn, struct tc_action *a)
struct tcf_idrinfo *idrinfo = tn->idrinfo;
spin_lock_bh(&idrinfo->lock);
- idr_replace_ext(&idrinfo->action_idr, a, a->tcfa_index);
+ idr_replace(&idrinfo->action_idr, a, a->tcfa_index);
spin_unlock_bh(&idrinfo->lock);
}
EXPORT_SYMBOL(tcf_idr_insert);
@@ -361,7 +351,7 @@ void tcf_idrinfo_destroy(const struct tc_action_ops *ops,
int ret;
unsigned long id = 1;
- idr_for_each_entry_ext(idr, p, id) {
+ idr_for_each_entry_ul(idr, p, id) {
ret = __tcf_idr_release(p, false, true);
if (ret == ACT_P_DELETED)
module_put(ops->owner);
diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c
index bcb4ccb5f894..2bc1bc23d42e 100644
--- a/net/sched/cls_api.c
+++ b/net/sched/cls_api.c
@@ -381,8 +381,8 @@ static int tcf_block_insert(struct tcf_block *block, struct net *net,
struct tcf_net *tn = net_generic(net, tcf_net_id);
int err;
- err = idr_alloc_ext(&tn->idr, block, NULL, block_index,
- block_index + 1, GFP_KERNEL);
+ err = idr_alloc_u32(&tn->idr, block, &block_index, block_index,
+ GFP_KERNEL);
if (err)
return err;
block->index = block_index;
@@ -393,7 +393,7 @@ static void tcf_block_remove(struct tcf_block *block, struct net *net)
{
struct tcf_net *tn = net_generic(net, tcf_net_id);
- idr_remove_ext(&tn->idr, block->index);
+ idr_remove(&tn->idr, block->index);
}
static struct tcf_block *tcf_block_create(struct net *net, struct Qdisc *q,
@@ -434,7 +434,7 @@ static struct tcf_block *tcf_block_lookup(struct net *net, u32 block_index)
{
struct tcf_net *tn = net_generic(net, tcf_net_id);
- return idr_find_ext(&tn->idr, block_index);
+ return idr_find(&tn->idr, block_index);
}
static struct tcf_chain *tcf_block_chain_zero(struct tcf_block *block)
diff --git a/net/sched/cls_basic.c b/net/sched/cls_basic.c
index d333f5c5101d..6b7ab3512f5b 100644
--- a/net/sched/cls_basic.c
+++ b/net/sched/cls_basic.c
@@ -120,7 +120,7 @@ static void basic_destroy(struct tcf_proto *tp, struct netlink_ext_ack *extack)
list_for_each_entry_safe(f, n, &head->flist, link) {
list_del_rcu(&f->link);
tcf_unbind_filter(tp, &f->res);
- idr_remove_ext(&head->handle_idr, f->handle);
+ idr_remove(&head->handle_idr, f->handle);
if (tcf_exts_get_net(&f->exts))
call_rcu(&f->rcu, basic_delete_filter);
else
@@ -138,7 +138,7 @@ static int basic_delete(struct tcf_proto *tp, void *arg, bool *last,
list_del_rcu(&f->link);
tcf_unbind_filter(tp, &f->res);
- idr_remove_ext(&head->handle_idr, f->handle);
+ idr_remove(&head->handle_idr, f->handle);
tcf_exts_get_net(&f->exts);
call_rcu(&f->rcu, basic_delete_filter);
*last = list_empty(&head->flist);
@@ -185,7 +185,6 @@ static int basic_change(struct net *net, struct sk_buff *in_skb,
struct nlattr *tb[TCA_BASIC_MAX + 1];
struct basic_filter *fold = (struct basic_filter *) *arg;
struct basic_filter *fnew;
- unsigned long idr_index;
if (tca[TCA_OPTIONS] == NULL)
return -EINVAL;
@@ -208,34 +207,30 @@ static int basic_change(struct net *net, struct sk_buff *in_skb,
if (err < 0)
goto errout;
- if (handle) {
- fnew->handle = handle;
- if (!fold) {
- err = idr_alloc_ext(&head->handle_idr, fnew, &idr_index,
- handle, handle + 1, GFP_KERNEL);
- if (err)
- goto errout;
- }
- } else {
- err = idr_alloc_ext(&head->handle_idr, fnew, &idr_index,
- 1, 0x7FFFFFFF, GFP_KERNEL);
- if (err)
- goto errout;
- fnew->handle = idr_index;
+ if (!handle) {
+ handle = 1;
+ err = idr_alloc_u32(&head->handle_idr, fnew, &handle,
+ INT_MAX, GFP_KERNEL);
+ } else if (!fold) {
+ err = idr_alloc_u32(&head->handle_idr, fnew, &handle,
+ handle, GFP_KERNEL);
}
+ if (err)
+ goto errout;
+ fnew->handle = handle;
err = basic_set_parms(net, tp, fnew, base, tb, tca[TCA_RATE], ovr,
extack);
if (err < 0) {
if (!fold)
- idr_remove_ext(&head->handle_idr, fnew->handle);
+ idr_remove(&head->handle_idr, fnew->handle);
goto errout;
}
*arg = fnew;
if (fold) {
- idr_replace_ext(&head->handle_idr, fnew, fnew->handle);
+ idr_replace(&head->handle_idr, fnew, fnew->handle);
list_replace_rcu(&fold->link, &fnew->link);
tcf_unbind_filter(tp, &fold->res);
tcf_exts_get_net(&fold->exts);
diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c
index 8e5326bc6440..b07c1fa8bc0d 100644
--- a/net/sched/cls_bpf.c
+++ b/net/sched/cls_bpf.c
@@ -295,7 +295,7 @@ static void __cls_bpf_delete(struct tcf_proto *tp, struct cls_bpf_prog *prog,
{
struct cls_bpf_head *head = rtnl_dereference(tp->root);
- idr_remove_ext(&head->handle_idr, prog->handle);
+ idr_remove(&head->handle_idr, prog->handle);
cls_bpf_stop_offload(tp, prog, extack);
list_del_rcu(&prog->link);
tcf_unbind_filter(tp, &prog->res);
@@ -471,7 +471,6 @@ static int cls_bpf_change(struct net *net, struct sk_buff *in_skb,
struct cls_bpf_prog *oldprog = *arg;
struct nlattr *tb[TCA_BPF_MAX + 1];
struct cls_bpf_prog *prog;
- unsigned long idr_index;
int ret;
if (tca[TCA_OPTIONS] == NULL)
@@ -498,21 +497,18 @@ static int cls_bpf_change(struct net *net, struct sk_buff *in_skb,
}
if (handle == 0) {
- ret = idr_alloc_ext(&head->handle_idr, prog, &idr_index,
- 1, 0x7FFFFFFF, GFP_KERNEL);
- if (ret)
- goto errout;
- prog->handle = idr_index;
- } else {
- if (!oldprog) {
- ret = idr_alloc_ext(&head->handle_idr, prog, &idr_index,
- handle, handle + 1, GFP_KERNEL);
- if (ret)
- goto errout;
- }
- prog->handle = handle;
+ handle = 1;
+ ret = idr_alloc_u32(&head->handle_idr, prog, &handle,
+ INT_MAX, GFP_KERNEL);
+ } else if (!oldprog) {
+ ret = idr_alloc_u32(&head->handle_idr, prog, &handle,
+ handle, GFP_KERNEL);
}
+ if (ret)
+ goto errout;
+ prog->handle = handle;
+
ret = cls_bpf_set_parms(net, tp, prog, base, tb, tca[TCA_RATE], ovr,
extack);
if (ret < 0)
@@ -526,7 +522,7 @@ static int cls_bpf_change(struct net *net, struct sk_buff *in_skb,
prog->gen_flags |= TCA_CLS_FLAGS_NOT_IN_HW;
if (oldprog) {
- idr_replace_ext(&head->handle_idr, prog, handle);
+ idr_replace(&head->handle_idr, prog, handle);
list_replace_rcu(&oldprog->link, &prog->link);
tcf_unbind_filter(tp, &oldprog->res);
tcf_exts_get_net(&oldprog->exts);
@@ -542,7 +538,7 @@ errout_parms:
cls_bpf_free_parms(prog);
errout_idr:
if (!oldprog)
- idr_remove_ext(&head->handle_idr, prog->handle);
+ idr_remove(&head->handle_idr, prog->handle);
errout:
tcf_exts_destroy(&prog->exts);
kfree(prog);
diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c
index dc9acaafc0a8..7d0ce2c40f93 100644
--- a/net/sched/cls_flower.c
+++ b/net/sched/cls_flower.c
@@ -288,7 +288,7 @@ static void __fl_delete(struct tcf_proto *tp, struct cls_fl_filter *f,
{
struct cls_fl_head *head = rtnl_dereference(tp->root);
- idr_remove_ext(&head->handle_idr, f->handle);
+ idr_remove(&head->handle_idr, f->handle);
list_del_rcu(&f->list);
if (!tc_skip_hw(f->flags))
fl_hw_destroy_filter(tp, f, extack);
@@ -334,7 +334,7 @@ static void *fl_get(struct tcf_proto *tp, u32 handle)
{
struct cls_fl_head *head = rtnl_dereference(tp->root);
- return idr_find_ext(&head->handle_idr, handle);
+ return idr_find(&head->handle_idr, handle);
}
static const struct nla_policy fl_policy[TCA_FLOWER_MAX + 1] = {
@@ -865,7 +865,6 @@ static int fl_change(struct net *net, struct sk_buff *in_skb,
struct cls_fl_filter *fnew;
struct nlattr **tb;
struct fl_flow_mask mask = {};
- unsigned long idr_index;
int err;
if (!tca[TCA_OPTIONS])
@@ -896,21 +895,17 @@ static int fl_change(struct net *net, struct sk_buff *in_skb,
goto errout;
if (!handle) {
- err = idr_alloc_ext(&head->handle_idr, fnew, &idr_index,
- 1, 0x80000000, GFP_KERNEL);
- if (err)
- goto errout;
- fnew->handle = idr_index;
- }
-
- /* user specifies a handle and it doesn't exist */
- if (handle && !fold) {
- err = idr_alloc_ext(&head->handle_idr, fnew, &idr_index,
- handle, handle + 1, GFP_KERNEL);
- if (err)
- goto errout;
- fnew->handle = idr_index;
+ handle = 1;
+ err = idr_alloc_u32(&head->handle_idr, fnew, &handle,
+ INT_MAX, GFP_KERNEL);
+ } else if (!fold) {
+ /* user specifies a handle and it doesn't exist */
+ err = idr_alloc_u32(&head->handle_idr, fnew, &handle,
+ handle, GFP_KERNEL);
}
+ if (err)
+ goto errout;
+ fnew->handle = handle;
if (tb[TCA_FLOWER_FLAGS]) {
fnew->flags = nla_get_u32(tb[TCA_FLOWER_FLAGS]);
@@ -966,8 +961,7 @@ static int fl_change(struct net *net, struct sk_buff *in_skb,
*arg = fnew;
if (fold) {
- fnew->handle = handle;
- idr_replace_ext(&head->handle_idr, fnew, fnew->handle);
+ idr_replace(&head->handle_idr, fnew, fnew->handle);
list_replace_rcu(&fold->list, &fnew->list);
tcf_unbind_filter(tp, &fold->res);
tcf_exts_get_net(&fold->exts);
@@ -981,7 +975,7 @@ static int fl_change(struct net *net, struct sk_buff *in_skb,
errout_idr:
if (fnew->handle)
- idr_remove_ext(&head->handle_idr, fnew->handle);
+ idr_remove(&head->handle_idr, fnew->handle);
errout:
tcf_exts_destroy(&fnew->exts);
kfree(fnew);
diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c
index 60c892c36a60..6c7601a530e3 100644
--- a/net/sched/cls_u32.c
+++ b/net/sched/cls_u32.c
@@ -316,19 +316,13 @@ static void *u32_get(struct tcf_proto *tp, u32 handle)
return u32_lookup_key(ht, handle);
}
+/* Protected by rtnl lock */
static u32 gen_new_htid(struct tc_u_common *tp_c, struct tc_u_hnode *ptr)
{
- unsigned long idr_index;
- int err;
-
- /* This is only used inside rtnl lock it is safe to increment
- * without read _copy_ update semantics
- */
- err = idr_alloc_ext(&tp_c->handle_idr, ptr, &idr_index,
- 1, 0x7FF, GFP_KERNEL);
- if (err)
+ int id = idr_alloc_cyclic(&tp_c->handle_idr, ptr, 1, 0x7FF, GFP_KERNEL);
+ if (id < 0)
return 0;
- return (u32)(idr_index | 0x800) << 20;
+ return (id | 0x800U) << 20;
}
static struct hlist_head *tc_u_common_hash;
@@ -398,10 +392,12 @@ static int u32_init(struct tcf_proto *tp)
static int u32_destroy_key(struct tcf_proto *tp, struct tc_u_knode *n,
bool free_pf)
{
+ struct tc_u_hnode *ht = rtnl_dereference(n->ht_down);
+
tcf_exts_destroy(&n->exts);
tcf_exts_put_net(&n->exts);
- if (n->ht_down)
- n->ht_down->refcnt--;
+ if (ht && --ht->refcnt == 0)
+ kfree(ht);
#ifdef CONFIG_CLS_U32_PERF
if (free_pf)
free_percpu(n->pf);
@@ -548,6 +544,7 @@ static void u32_remove_hw_knode(struct tcf_proto *tp, struct tc_u_knode *n,
static int u32_replace_hw_knode(struct tcf_proto *tp, struct tc_u_knode *n,
u32 flags, struct netlink_ext_ack *extack)
{
+ struct tc_u_hnode *ht = rtnl_dereference(n->ht_down);
struct tcf_block *block = tp->chain->block;
struct tc_cls_u32_offload cls_u32 = {};
bool skip_sw = tc_skip_sw(flags);
@@ -567,7 +564,7 @@ static int u32_replace_hw_knode(struct tcf_proto *tp, struct tc_u_knode *n,
cls_u32.knode.sel = &n->sel;
cls_u32.knode.exts = &n->exts;
if (n->ht_down)
- cls_u32.knode.link_handle = n->ht_down->handle;
+ cls_u32.knode.link_handle = ht->handle;
err = tc_setup_cb_call(block, NULL, TC_SETUP_CLSU32, &cls_u32, skip_sw);
if (err < 0) {
@@ -595,7 +592,7 @@ static void u32_clear_hnode(struct tcf_proto *tp, struct tc_u_hnode *ht,
rtnl_dereference(n->next));
tcf_unbind_filter(tp, &n->res);
u32_remove_hw_knode(tp, n, extack);
- idr_remove_ext(&ht->handle_idr, n->handle);
+ idr_remove(&ht->handle_idr, n->handle);
if (tcf_exts_get_net(&n->exts))
call_rcu(&n->rcu, u32_delete_key_freepf_rcu);
else
@@ -622,7 +619,7 @@ static int u32_destroy_hnode(struct tcf_proto *tp, struct tc_u_hnode *ht,
if (phn == ht) {
u32_clear_hw_hnode(tp, ht, extack);
idr_destroy(&ht->handle_idr);
- idr_remove_ext(&tp_c->handle_idr, ht->handle);
+ idr_remove(&tp_c->handle_idr, ht->handle);
RCU_INIT_POINTER(*hn, ht->next);
kfree_rcu(ht, rcu);
return 0;
@@ -658,16 +655,15 @@ static void u32_destroy(struct tcf_proto *tp, struct netlink_ext_ack *extack)
hlist_del(&tp_c->hnode);
- for (ht = rtnl_dereference(tp_c->hlist);
- ht;
- ht = rtnl_dereference(ht->next)) {
- ht->refcnt--;
- u32_clear_hnode(tp, ht, extack);
- }
-
while ((ht = rtnl_dereference(tp_c->hlist)) != NULL) {
+ u32_clear_hnode(tp, ht, extack);
RCU_INIT_POINTER(tp_c->hlist, ht->next);
- kfree_rcu(ht, rcu);
+
+ /* u32_destroy_key() will later free ht for us, if it's
+ * still referenced by some knode
+ */
+ if (--ht->refcnt == 0)
+ kfree_rcu(ht, rcu);
}
idr_destroy(&tp_c->handle_idr);
@@ -745,19 +741,17 @@ ret:
static u32 gen_new_kid(struct tc_u_hnode *ht, u32 htid)
{
- unsigned long idr_index;
- u32 start = htid | 0x800;
+ u32 index = htid | 0x800;
u32 max = htid | 0xFFF;
- u32 min = htid;
- if (idr_alloc_ext(&ht->handle_idr, NULL, &idr_index,
- start, max + 1, GFP_KERNEL)) {
- if (idr_alloc_ext(&ht->handle_idr, NULL, &idr_index,
- min + 1, max + 1, GFP_KERNEL))
- return max;
+ if (idr_alloc_u32(&ht->handle_idr, NULL, &index, max, GFP_KERNEL)) {
+ index = htid + 1;
+ if (idr_alloc_u32(&ht->handle_idr, NULL, &index, max,
+ GFP_KERNEL))
+ index = max;
}
- return (u32)idr_index;
+ return index;
}
static const struct nla_policy u32_policy[TCA_U32_MAX + 1] = {
@@ -847,7 +841,7 @@ static void u32_replace_knode(struct tcf_proto *tp, struct tc_u_common *tp_c,
if (pins->handle == n->handle)
break;
- idr_replace_ext(&ht->handle_idr, n, n->handle);
+ idr_replace(&ht->handle_idr, n, n->handle);
RCU_INIT_POINTER(n->next, pins->next);
rcu_assign_pointer(*ins, n);
}
@@ -855,8 +849,9 @@ static void u32_replace_knode(struct tcf_proto *tp, struct tc_u_common *tp_c,
static struct tc_u_knode *u32_init_knode(struct tcf_proto *tp,
struct tc_u_knode *n)
{
- struct tc_u_knode *new;
+ struct tc_u_hnode *ht = rtnl_dereference(n->ht_down);
struct tc_u32_sel *s = &n->sel;
+ struct tc_u_knode *new;
new = kzalloc(sizeof(*n) + s->nkeys*sizeof(struct tc_u32_key),
GFP_KERNEL);
@@ -874,11 +869,11 @@ static struct tc_u_knode *u32_init_knode(struct tcf_proto *tp,
new->fshift = n->fshift;
new->res = n->res;
new->flags = n->flags;
- RCU_INIT_POINTER(new->ht_down, n->ht_down);
+ RCU_INIT_POINTER(new->ht_down, ht);
/* bump reference count as long as we hold pointer to structure */
- if (new->ht_down)
- new->ht_down->refcnt++;
+ if (ht)
+ ht->refcnt++;
#ifdef CONFIG_CLS_U32_PERF
/* Statistics may be incremented by readers during update
@@ -952,7 +947,8 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,
return -EINVAL;
}
- if (n->flags != flags) {
+ if ((n->flags ^ flags) &
+ ~(TCA_CLS_FLAGS_IN_HW | TCA_CLS_FLAGS_NOT_IN_HW)) {
NL_SET_ERR_MSG_MOD(extack, "Key node flags do not match passed flags");
return -EINVAL;
}
@@ -1007,8 +1003,8 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,
return -ENOMEM;
}
} else {
- err = idr_alloc_ext(&tp_c->handle_idr, ht, NULL,
- handle, handle + 1, GFP_KERNEL);
+ err = idr_alloc_u32(&tp_c->handle_idr, ht, &handle,
+ handle, GFP_KERNEL);
if (err) {
kfree(ht);
return err;
@@ -1024,7 +1020,7 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,
err = u32_replace_hw_hnode(tp, ht, flags, extack);
if (err) {
- idr_remove_ext(&tp_c->handle_idr, handle);
+ idr_remove(&tp_c->handle_idr, handle);
kfree(ht);
return err;
}
@@ -1064,8 +1060,7 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,
return -EINVAL;
}
handle = htid | TC_U32_NODE(handle);
- err = idr_alloc_ext(&ht->handle_idr, NULL, NULL,
- handle, handle + 1,
+ err = idr_alloc_u32(&ht->handle_idr, NULL, &handle, handle,
GFP_KERNEL);
if (err)
return err;
@@ -1160,7 +1155,7 @@ errfree:
#endif
kfree(n);
erridr:
- idr_remove_ext(&ht->handle_idr, handle);
+ idr_remove(&ht->handle_idr, handle);
return err;
}
diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c
index 7bbc13b8ca47..7c179addebcd 100644
--- a/net/sched/sch_netem.c
+++ b/net/sched/sch_netem.c
@@ -327,7 +327,7 @@ static s64 tabledist(s64 mu, s32 sigma,
/* default uniform distribution */
if (dist == NULL)
- return (rnd % (2 * sigma)) - sigma + mu;
+ return ((rnd % (2 * sigma)) + mu) - sigma;
t = dist->table[rnd % dist->size];
x = (sigma % NETEM_DIST_SCALE) * t;
diff --git a/net/sched/sch_tbf.c b/net/sched/sch_tbf.c
index 83e76d046993..229172d509cc 100644
--- a/net/sched/sch_tbf.c
+++ b/net/sched/sch_tbf.c
@@ -142,16 +142,6 @@ static u64 psched_ns_t2l(const struct psched_ratecfg *r,
return len;
}
-/*
- * Return length of individual segments of a gso packet,
- * including all headers (MAC, IP, TCP/UDP)
- */
-static unsigned int skb_gso_mac_seglen(const struct sk_buff *skb)
-{
- unsigned int hdr_len = skb_transport_header(skb) - skb_mac_header(skb);
- return hdr_len + skb_gso_transport_seglen(skb);
-}
-
/* GSO packet is too big, segment it so that tbf can transmit
* each segment in time
*/
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
index 5d4c15bf66d2..e35d4f73d2df 100644
--- a/net/sctp/ipv6.c
+++ b/net/sctp/ipv6.c
@@ -326,8 +326,10 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr,
final_p = fl6_update_dst(fl6, rcu_dereference(np->opt), &final);
bdst = ip6_dst_lookup_flow(sk, fl6, final_p);
- if (!IS_ERR(bdst) &&
- ipv6_chk_addr(dev_net(bdst->dev),
+ if (IS_ERR(bdst))
+ continue;
+
+ if (ipv6_chk_addr(dev_net(bdst->dev),
&laddr->a.v6.sin6_addr, bdst->dev, 1)) {
if (!IS_ERR_OR_NULL(dst))
dst_release(dst);
@@ -336,8 +338,10 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr,
}
bmatchlen = sctp_v6_addr_match_len(daddr, &laddr->a);
- if (matchlen > bmatchlen)
+ if (matchlen > bmatchlen) {
+ dst_release(bdst);
continue;
+ }
if (!IS_ERR_OR_NULL(dst))
dst_release(dst);
diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c
index 6a38c2503649..91813e686c67 100644
--- a/net/sctp/protocol.c
+++ b/net/sctp/protocol.c
@@ -514,22 +514,20 @@ static void sctp_v4_get_dst(struct sctp_transport *t, union sctp_addr *saddr,
if (IS_ERR(rt))
continue;
- if (!dst)
- dst = &rt->dst;
-
/* Ensure the src address belongs to the output
* interface.
*/
odev = __ip_dev_find(sock_net(sk), laddr->a.v4.sin_addr.s_addr,
false);
if (!odev || odev->ifindex != fl4->flowi4_oif) {
- if (&rt->dst != dst)
+ if (!dst)
+ dst = &rt->dst;
+ else
dst_release(&rt->dst);
continue;
}
- if (dst != &rt->dst)
- dst_release(dst);
+ dst_release(dst);
dst = &rt->dst;
break;
}
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c
index 793b05ec692b..d01475f5f710 100644
--- a/net/sctp/sm_make_chunk.c
+++ b/net/sctp/sm_make_chunk.c
@@ -1380,9 +1380,14 @@ static struct sctp_chunk *_sctp_make_chunk(const struct sctp_association *asoc,
struct sctp_chunk *retval;
struct sk_buff *skb;
struct sock *sk;
+ int chunklen;
+
+ chunklen = SCTP_PAD4(sizeof(*chunk_hdr) + paylen);
+ if (chunklen > SCTP_MAX_CHUNK_LEN)
+ goto nodata;
/* No need to allocate LL here, as this is only a chunk. */
- skb = alloc_skb(SCTP_PAD4(sizeof(*chunk_hdr) + paylen), gfp);
+ skb = alloc_skb(chunklen, gfp);
if (!skb)
goto nodata;
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index 356e387f82e7..ebb8cb9eb0bd 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -5053,7 +5053,7 @@ static int sctp_getsockopt_autoclose(struct sock *sk, int len, char __user *optv
len = sizeof(int);
if (put_user(len, optlen))
return -EFAULT;
- if (copy_to_user(optval, &sctp_sk(sk)->autoclose, len))
+ if (put_user(sctp_sk(sk)->autoclose, (int __user *)optval))
return -EFAULT;
return 0;
}
@@ -8552,6 +8552,10 @@ struct proto sctp_prot = {
.unhash = sctp_unhash,
.get_port = sctp_get_port,
.obj_size = sizeof(struct sctp_sock),
+ .useroffset = offsetof(struct sctp_sock, subscribe),
+ .usersize = offsetof(struct sctp_sock, initmsg) -
+ offsetof(struct sctp_sock, subscribe) +
+ sizeof_field(struct sctp_sock, initmsg),
.sysctl_mem = sysctl_sctp_mem,
.sysctl_rmem = sysctl_sctp_rmem,
.sysctl_wmem = sysctl_sctp_wmem,
@@ -8591,6 +8595,10 @@ struct proto sctpv6_prot = {
.unhash = sctp_unhash,
.get_port = sctp_get_port,
.obj_size = sizeof(struct sctp6_sock),
+ .useroffset = offsetof(struct sctp6_sock, sctp.subscribe),
+ .usersize = offsetof(struct sctp6_sock, sctp.initmsg) -
+ offsetof(struct sctp6_sock, sctp.subscribe) +
+ sizeof_field(struct sctp6_sock, sctp.initmsg),
.sysctl_mem = sysctl_sctp_mem,
.sysctl_rmem = sysctl_sctp_rmem,
.sysctl_wmem = sysctl_sctp_wmem,
diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c
index 896691afbb1a..d9db2eab3a8d 100644
--- a/net/sunrpc/sched.c
+++ b/net/sunrpc/sched.c
@@ -461,6 +461,18 @@ static void rpc_wake_up_task_queue_locked(struct rpc_wait_queue *queue, struct r
/*
* Wake up a task on a specific queue
*/
+void rpc_wake_up_queued_task_on_wq(struct workqueue_struct *wq,
+ struct rpc_wait_queue *queue,
+ struct rpc_task *task)
+{
+ spin_lock_bh(&queue->lock);
+ rpc_wake_up_task_on_wq_queue_locked(wq, queue, task);
+ spin_unlock_bh(&queue->lock);
+}
+
+/*
+ * Wake up a task on a specific queue
+ */
void rpc_wake_up_queued_task(struct rpc_wait_queue *queue, struct rpc_task *task)
{
spin_lock_bh(&queue->lock);
@@ -1092,12 +1104,12 @@ static int rpciod_start(void)
* Create the rpciod thread and wait for it to start.
*/
dprintk("RPC: creating workqueue rpciod\n");
- wq = alloc_workqueue("rpciod", WQ_MEM_RECLAIM, 0);
+ wq = alloc_workqueue("rpciod", WQ_MEM_RECLAIM | WQ_UNBOUND, 0);
if (!wq)
goto out_failed;
rpciod_workqueue = wq;
/* Note: highpri because network receive is latency sensitive */
- wq = alloc_workqueue("xprtiod", WQ_MEM_RECLAIM | WQ_HIGHPRI, 0);
+ wq = alloc_workqueue("xprtiod", WQ_UNBOUND|WQ_MEM_RECLAIM|WQ_HIGHPRI, 0);
if (!wq)
goto free_rpciod;
xprtiod_workqueue = wq;
diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c
index 5570719e4787..943f2a745cd5 100644
--- a/net/sunrpc/svcsock.c
+++ b/net/sunrpc/svcsock.c
@@ -384,25 +384,11 @@ static int svc_partial_recvfrom(struct svc_rqst *rqstp,
static void svc_sock_setbufsize(struct socket *sock, unsigned int snd,
unsigned int rcv)
{
-#if 0
- mm_segment_t oldfs;
- oldfs = get_fs(); set_fs(KERNEL_DS);
- sock_setsockopt(sock, SOL_SOCKET, SO_SNDBUF,
- (char*)&snd, sizeof(snd));
- sock_setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
- (char*)&rcv, sizeof(rcv));
-#else
- /* sock_setsockopt limits use to sysctl_?mem_max,
- * which isn't acceptable. Until that is made conditional
- * on not having CAP_SYS_RESOURCE or similar, we go direct...
- * DaveM said I could!
- */
lock_sock(sock->sk);
sock->sk->sk_sndbuf = snd * 2;
sock->sk->sk_rcvbuf = rcv * 2;
sock->sk->sk_write_space(sock->sk);
release_sock(sock->sk);
-#endif
}
static int svc_sock_secure_port(struct svc_rqst *rqstp)
diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c
index 2436fd1125fc..8f0ad4f268da 100644
--- a/net/sunrpc/xprt.c
+++ b/net/sunrpc/xprt.c
@@ -517,7 +517,8 @@ void xprt_write_space(struct rpc_xprt *xprt)
if (xprt->snd_task) {
dprintk("RPC: write space: waking waiting task on "
"xprt %p\n", xprt);
- rpc_wake_up_queued_task(&xprt->pending, xprt->snd_task);
+ rpc_wake_up_queued_task_on_wq(xprtiod_workqueue,
+ &xprt->pending, xprt->snd_task);
}
spin_unlock_bh(&xprt->transport_lock);
}
diff --git a/net/sunrpc/xprtrdma/rpc_rdma.c b/net/sunrpc/xprtrdma/rpc_rdma.c
index 162e5dd82466..f0855a959a27 100644
--- a/net/sunrpc/xprtrdma/rpc_rdma.c
+++ b/net/sunrpc/xprtrdma/rpc_rdma.c
@@ -143,7 +143,7 @@ static bool rpcrdma_args_inline(struct rpcrdma_xprt *r_xprt,
if (xdr->page_len) {
remaining = xdr->page_len;
offset = offset_in_page(xdr->page_base);
- count = 0;
+ count = RPCRDMA_MIN_SEND_SGES;
while (remaining) {
remaining -= min_t(unsigned int,
PAGE_SIZE - offset, remaining);
diff --git a/net/sunrpc/xprtrdma/svc_rdma_backchannel.c b/net/sunrpc/xprtrdma/svc_rdma_backchannel.c
index af7893501e40..a73632ca9048 100644
--- a/net/sunrpc/xprtrdma/svc_rdma_backchannel.c
+++ b/net/sunrpc/xprtrdma/svc_rdma_backchannel.c
@@ -95,7 +95,6 @@ out_shortreply:
out_notfound:
dprintk("svcrdma: unrecognized bc reply: xprt=%p, xid=%08x\n",
xprt, be32_to_cpu(xid));
-
goto out_unlock;
}
@@ -129,10 +128,6 @@ static int svc_rdma_bc_sendto(struct svcxprt_rdma *rdma,
if (ret < 0)
goto out_err;
- ret = svc_rdma_repost_recv(rdma, GFP_NOIO);
- if (ret)
- goto out_err;
-
/* Bump page refcnt so Send completion doesn't release
* the rq_buffer before all retransmits are complete.
*/
diff --git a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c
index ad4bd62eebf1..19e9c6b33042 100644
--- a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c
+++ b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c
@@ -400,10 +400,6 @@ static void svc_rdma_send_error(struct svcxprt_rdma *xprt,
struct page *page;
int ret;
- ret = svc_rdma_repost_recv(xprt, GFP_KERNEL);
- if (ret)
- return;
-
page = alloc_page(GFP_KERNEL);
if (!page)
return;
@@ -554,8 +550,6 @@ int svc_rdma_recvfrom(struct svc_rqst *rqstp)
ret = svc_rdma_handle_bc_reply(xprt->xpt_bc_xprt, p,
&rqstp->rq_arg);
svc_rdma_put_context(ctxt, 0);
- if (ret)
- goto repost;
return ret;
}
@@ -590,6 +584,5 @@ out_postfail:
out_drop:
svc_rdma_put_context(ctxt, 1);
-repost:
- return svc_rdma_repost_recv(rdma_xprt, GFP_KERNEL);
+ return 0;
}
diff --git a/net/sunrpc/xprtrdma/svc_rdma_rw.c b/net/sunrpc/xprtrdma/svc_rdma_rw.c
index 9bd04549a1ad..12b9a7e0b6d2 100644
--- a/net/sunrpc/xprtrdma/svc_rdma_rw.c
+++ b/net/sunrpc/xprtrdma/svc_rdma_rw.c
@@ -727,12 +727,16 @@ static int svc_rdma_build_normal_read_chunk(struct svc_rqst *rqstp,
head->arg.head[0].iov_len - info->ri_position;
head->arg.head[0].iov_len = info->ri_position;
- /* Read chunk may need XDR roundup (see RFC 5666, s. 3.7).
+ /* Read chunk may need XDR roundup (see RFC 8166, s. 3.4.5.2).
*
- * NFSv2/3 write decoders need the length of the tail to
- * contain the size of the roundup padding.
+ * If the client already rounded up the chunk length, the
+ * length does not change. Otherwise, the length of the page
+ * list is increased to include XDR round-up.
+ *
+ * Currently these chunks always start at page offset 0,
+ * thus the rounded-up length never crosses a page boundary.
*/
- head->arg.tail[0].iov_len += 4 - (info->ri_chunklen & 3);
+ info->ri_chunklen = XDR_QUADLEN(info->ri_chunklen) << 2;
head->arg.page_len = info->ri_chunklen;
head->arg.len += info->ri_chunklen;
diff --git a/net/sunrpc/xprtrdma/svc_rdma_sendto.c b/net/sunrpc/xprtrdma/svc_rdma_sendto.c
index 7c3a211e0e9a..649441d5087d 100644
--- a/net/sunrpc/xprtrdma/svc_rdma_sendto.c
+++ b/net/sunrpc/xprtrdma/svc_rdma_sendto.c
@@ -674,9 +674,6 @@ int svc_rdma_sendto(struct svc_rqst *rqstp)
svc_rdma_xdr_encode_reply_chunk(rdma_resp, rp_ch, ret);
}
- ret = svc_rdma_post_recv(rdma, GFP_KERNEL);
- if (ret)
- goto err1;
ret = svc_rdma_send_reply_msg(rdma, rdma_argp, rdma_resp, rqstp,
wr_lst, rp_ch);
if (ret < 0)
@@ -687,9 +684,6 @@ int svc_rdma_sendto(struct svc_rqst *rqstp)
if (ret != -E2BIG && ret != -EINVAL)
goto err1;
- ret = svc_rdma_post_recv(rdma, GFP_KERNEL);
- if (ret)
- goto err1;
ret = svc_rdma_send_error_msg(rdma, rdma_resp, rqstp);
if (ret < 0)
goto err0;
diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c
index 46ec069150d5..9ad12a215b51 100644
--- a/net/sunrpc/xprtrdma/svc_rdma_transport.c
+++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c
@@ -58,6 +58,7 @@
#define RPCDBG_FACILITY RPCDBG_SVCXPRT
+static int svc_rdma_post_recv(struct svcxprt_rdma *xprt);
static struct svcxprt_rdma *rdma_create_xprt(struct svc_serv *, int);
static struct svc_xprt *svc_rdma_create(struct svc_serv *serv,
struct net *net,
@@ -320,6 +321,8 @@ static void svc_rdma_wc_receive(struct ib_cq *cq, struct ib_wc *wc)
list_add_tail(&ctxt->list, &xprt->sc_rq_dto_q);
spin_unlock(&xprt->sc_rq_dto_lock);
+ svc_rdma_post_recv(xprt);
+
set_bit(XPT_DATA, &xprt->sc_xprt.xpt_flags);
if (test_bit(RDMAXPRT_CONN_PENDING, &xprt->sc_flags))
goto out;
@@ -404,7 +407,8 @@ static struct svcxprt_rdma *rdma_create_xprt(struct svc_serv *serv,
return cma_xprt;
}
-int svc_rdma_post_recv(struct svcxprt_rdma *xprt, gfp_t flags)
+static int
+svc_rdma_post_recv(struct svcxprt_rdma *xprt)
{
struct ib_recv_wr recv_wr, *bad_recv_wr;
struct svc_rdma_op_ctxt *ctxt;
@@ -423,7 +427,7 @@ int svc_rdma_post_recv(struct svcxprt_rdma *xprt, gfp_t flags)
pr_err("svcrdma: Too many sges (%d)\n", sge_no);
goto err_put_ctxt;
}
- page = alloc_page(flags);
+ page = alloc_page(GFP_KERNEL);
if (!page)
goto err_put_ctxt;
ctxt->pages[sge_no] = page;
@@ -459,21 +463,6 @@ int svc_rdma_post_recv(struct svcxprt_rdma *xprt, gfp_t flags)
return -ENOMEM;
}
-int svc_rdma_repost_recv(struct svcxprt_rdma *xprt, gfp_t flags)
-{
- int ret = 0;
-
- ret = svc_rdma_post_recv(xprt, flags);
- if (ret) {
- pr_err("svcrdma: could not post a receive buffer, err=%d.\n",
- ret);
- pr_err("svcrdma: closing transport %p.\n", xprt);
- set_bit(XPT_CLOSE, &xprt->sc_xprt.xpt_flags);
- ret = -ENOTCONN;
- }
- return ret;
-}
-
static void
svc_rdma_parse_connect_private(struct svcxprt_rdma *newxprt,
struct rdma_conn_param *param)
@@ -833,7 +822,7 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt)
/* Post receive buffers */
for (i = 0; i < newxprt->sc_max_requests; i++) {
- ret = svc_rdma_post_recv(newxprt, GFP_KERNEL);
+ ret = svc_rdma_post_recv(newxprt);
if (ret) {
dprintk("svcrdma: failure posting receive buffers\n");
goto errout;
diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c
index f4eb63e8e689..e6f84a6434a0 100644
--- a/net/sunrpc/xprtrdma/verbs.c
+++ b/net/sunrpc/xprtrdma/verbs.c
@@ -505,7 +505,7 @@ rpcrdma_ep_create(struct rpcrdma_ep *ep, struct rpcrdma_ia *ia,
pr_warn("rpcrdma: HCA provides only %d send SGEs\n", max_sge);
return -ENOMEM;
}
- ia->ri_max_send_sges = max_sge - RPCRDMA_MIN_SEND_SGES;
+ ia->ri_max_send_sges = max_sge;
if (ia->ri_device->attrs.max_qp_wr <= RPCRDMA_BACKWARD_WRS) {
dprintk("RPC: %s: insufficient wqe's available\n",
@@ -1502,6 +1502,9 @@ __rpcrdma_dma_map_regbuf(struct rpcrdma_ia *ia, struct rpcrdma_regbuf *rb)
static void
rpcrdma_dma_unmap_regbuf(struct rpcrdma_regbuf *rb)
{
+ if (!rb)
+ return;
+
if (!rpcrdma_regbuf_is_mapped(rb))
return;
@@ -1517,9 +1520,6 @@ rpcrdma_dma_unmap_regbuf(struct rpcrdma_regbuf *rb)
void
rpcrdma_free_regbuf(struct rpcrdma_regbuf *rb)
{
- if (!rb)
- return;
-
rpcrdma_dma_unmap_regbuf(rb);
kfree(rb);
}
diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c
index 18803021f242..a6b8c1f8f92a 100644
--- a/net/sunrpc/xprtsock.c
+++ b/net/sunrpc/xprtsock.c
@@ -807,13 +807,6 @@ static void xs_sock_reset_connection_flags(struct rpc_xprt *xprt)
smp_mb__after_atomic();
}
-static void xs_sock_mark_closed(struct rpc_xprt *xprt)
-{
- xs_sock_reset_connection_flags(xprt);
- /* Mark transport as closed and wake up all pending tasks */
- xprt_disconnect_done(xprt);
-}
-
/**
* xs_error_report - callback to handle TCP socket state errors
* @sk: socket
@@ -833,9 +826,6 @@ static void xs_error_report(struct sock *sk)
err = -sk->sk_err;
if (err == 0)
goto out;
- /* Is this a reset event? */
- if (sk->sk_state == TCP_CLOSE)
- xs_sock_mark_closed(xprt);
dprintk("RPC: xs_error_report client %p, error=%d...\n",
xprt, -err);
trace_rpc_socket_error(xprt, sk->sk_socket, err);
@@ -1078,18 +1068,18 @@ static void xs_udp_data_read_skb(struct rpc_xprt *xprt,
/* Suck it into the iovec, verify checksum if not done by hw. */
if (csum_partial_copy_to_xdr(&rovr->rq_private_buf, skb)) {
- __UDPX_INC_STATS(sk, UDP_MIB_INERRORS);
spin_lock(&xprt->recv_lock);
+ __UDPX_INC_STATS(sk, UDP_MIB_INERRORS);
goto out_unpin;
}
- __UDPX_INC_STATS(sk, UDP_MIB_INDATAGRAMS);
spin_lock_bh(&xprt->transport_lock);
xprt_adjust_cwnd(xprt, task, copied);
spin_unlock_bh(&xprt->transport_lock);
spin_lock(&xprt->recv_lock);
xprt_complete_rqst(task, copied);
+ __UDPX_INC_STATS(sk, UDP_MIB_INDATAGRAMS);
out_unpin:
xprt_unpin_rqst(rovr);
out_unlock:
@@ -1655,9 +1645,11 @@ static void xs_tcp_state_change(struct sock *sk)
if (test_and_clear_bit(XPRT_SOCK_CONNECTING,
&transport->sock_state))
xprt_clear_connecting(xprt);
+ clear_bit(XPRT_CLOSING, &xprt->state);
if (sk->sk_err)
xprt_wake_pending_tasks(xprt, -sk->sk_err);
- xs_sock_mark_closed(xprt);
+ /* Trigger the socket release */
+ xs_tcp_force_close(xprt);
}
out:
read_unlock_bh(&sk->sk_callback_lock);
@@ -2265,14 +2257,19 @@ static void xs_tcp_shutdown(struct rpc_xprt *xprt)
{
struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);
struct socket *sock = transport->sock;
+ int skst = transport->inet ? transport->inet->sk_state : TCP_CLOSE;
if (sock == NULL)
return;
- if (xprt_connected(xprt)) {
+ switch (skst) {
+ default:
kernel_sock_shutdown(sock, SHUT_RDWR);
trace_rpc_socket_shutdown(xprt, sock);
- } else
+ break;
+ case TCP_CLOSE:
+ case TCP_TIME_WAIT:
xs_reset_transport(transport);
+ }
}
static void xs_tcp_set_socket_timeouts(struct rpc_xprt *xprt,
diff --git a/net/tipc/msg.c b/net/tipc/msg.c
index 55d8ba92291d..4e1c6f6450bb 100644
--- a/net/tipc/msg.c
+++ b/net/tipc/msg.c
@@ -208,8 +208,8 @@ bool tipc_msg_validate(struct sk_buff **_skb)
int msz, hsz;
/* Ensure that flow control ratio condition is satisfied */
- if (unlikely(skb->truesize / buf_roundup_len(skb) > 4)) {
- skb = skb_copy(skb, GFP_ATOMIC);
+ if (unlikely(skb->truesize / buf_roundup_len(skb) >= 4)) {
+ skb = skb_copy_expand(skb, BUF_HEADROOM, 0, GFP_ATOMIC);
if (!skb)
return false;
kfree_skb(*_skb);
diff --git a/net/tls/tls_main.c b/net/tls/tls_main.c
index 736719c8314e..b0d5fcea47e7 100644
--- a/net/tls/tls_main.c
+++ b/net/tls/tls_main.c
@@ -484,6 +484,8 @@ out:
static struct tcp_ulp_ops tcp_tls_ulp_ops __read_mostly = {
.name = "tls",
+ .uid = TCP_ULP_TLS,
+ .user_visible = true,
.owner = THIS_MODULE,
.init = tls_init,
};
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index ab0c687d0c44..9c0dcc8324b0 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -16,6 +16,7 @@
#include <linux/nl80211.h>
#include <linux/rtnetlink.h>
#include <linux/netlink.h>
+#include <linux/nospec.h>
#include <linux/etherdevice.h>
#include <net/net_namespace.h>
#include <net/genetlink.h>
@@ -2080,20 +2081,22 @@ static const struct nla_policy txq_params_policy[NL80211_TXQ_ATTR_MAX + 1] = {
static int parse_txq_params(struct nlattr *tb[],
struct ieee80211_txq_params *txq_params)
{
+ u8 ac;
+
if (!tb[NL80211_TXQ_ATTR_AC] || !tb[NL80211_TXQ_ATTR_TXOP] ||
!tb[NL80211_TXQ_ATTR_CWMIN] || !tb[NL80211_TXQ_ATTR_CWMAX] ||
!tb[NL80211_TXQ_ATTR_AIFS])
return -EINVAL;
- txq_params->ac = nla_get_u8(tb[NL80211_TXQ_ATTR_AC]);
+ ac = nla_get_u8(tb[NL80211_TXQ_ATTR_AC]);
txq_params->txop = nla_get_u16(tb[NL80211_TXQ_ATTR_TXOP]);
txq_params->cwmin = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMIN]);
txq_params->cwmax = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMAX]);
txq_params->aifs = nla_get_u8(tb[NL80211_TXQ_ATTR_AIFS]);
- if (txq_params->ac >= NL80211_NUM_ACS)
+ if (ac >= NL80211_NUM_ACS)
return -EINVAL;
-
+ txq_params->ac = array_index_nospec(ac, NL80211_NUM_ACS);
return 0;
}