summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/batman-adv/bat_iv_ogm.c12
-rw-r--r--net/batman-adv/bat_v_elp.c9
-rw-r--r--net/batman-adv/bat_v_ogm.c33
-rw-r--r--net/batman-adv/bridge_loop_avoidance.c9
-rw-r--r--net/batman-adv/hard-interface.c165
-rw-r--r--net/batman-adv/hard-interface.h10
-rw-r--r--net/batman-adv/main.c9
-rw-r--r--net/batman-adv/main.h3
-rw-r--r--net/batman-adv/mesh-interface.c13
-rw-r--r--net/batman-adv/netlink.c4
-rw-r--r--net/batman-adv/originator.c4
-rw-r--r--net/batman-adv/tp_meter.c217
-rw-r--r--net/batman-adv/tvlv.c86
-rw-r--r--net/batman-adv/types.h28
-rw-r--r--net/bridge/netfilter/ebtables.c12
-rw-r--r--net/core/fib_rules.c82
-rw-r--r--net/core/netpoll.c2
-rw-r--r--net/ipv4/fib_frontend.c66
-rw-r--r--net/ipv4/fib_rules.c20
-rw-r--r--net/ipv4/fib_trie.c3
-rw-r--r--net/ipv4/igmp.c2
-rw-r--r--net/ipv4/netfilter/nf_nat_snmp_basic_main.c2
-rw-r--r--net/ipv4/raw.c41
-rw-r--r--net/ipv4/udp.c39
-rw-r--r--net/ipv6/addrconf.c1
-rw-r--r--net/ipv6/fib6_rules.c17
-rw-r--r--net/ipv6/mcast.c1
-rw-r--r--net/ipv6/udp.c19
-rw-r--r--net/netfilter/ipvs/ip_vs_nfct.c2
-rw-r--r--net/netfilter/nf_conntrack_amanda.c6
-rw-r--r--net/netfilter/nf_conntrack_broadcast.c2
-rw-r--r--net/netfilter/nf_conntrack_ftp.c32
-rw-r--r--net/netfilter/nf_conntrack_h323_main.c12
-rw-r--r--net/netfilter/nf_conntrack_helper.c77
-rw-r--r--net/netfilter/nf_conntrack_irc.c27
-rw-r--r--net/netfilter/nf_conntrack_netbios_ns.c2
-rw-r--r--net/netfilter/nf_conntrack_ovs.c6
-rw-r--r--net/netfilter/nf_conntrack_pptp.c2
-rw-r--r--net/netfilter/nf_conntrack_sane.c34
-rw-r--r--net/netfilter/nf_conntrack_sip.c45
-rw-r--r--net/netfilter/nf_conntrack_snmp.c4
-rw-r--r--net/netfilter/nf_conntrack_tftp.c33
-rw-r--r--net/netfilter/nf_nat_core.c6
-rw-r--r--net/netfilter/nf_nat_proto.c8
-rw-r--r--net/netfilter/nfnetlink_cthelper.c21
-rw-r--r--net/netfilter/nfnetlink_cttimeout.c2
-rw-r--r--net/netfilter/nfnetlink_hook.c37
-rw-r--r--net/netfilter/nft_ct.c35
-rw-r--r--net/netfilter/nft_set_rbtree.c3
-rw-r--r--net/netfilter/x_tables.c30
-rw-r--r--net/netfilter/xt_TCPOPTSTRIP.c8
-rw-r--r--net/netfilter/xt_dscp.c12
-rw-r--r--net/netfilter/xt_recent.c2
-rw-r--r--net/netfilter/xt_tcpmss.c13
-rw-r--r--net/sched/act_ct.c4
-rw-r--r--net/vmw_vsock/virtio_transport_common.c47
56 files changed, 708 insertions, 713 deletions
diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c
index bb2f012b454e..22622283f59b 100644
--- a/net/batman-adv/bat_iv_ogm.c
+++ b/net/batman-adv/bat_iv_ogm.c
@@ -404,23 +404,14 @@ static void batadv_iv_ogm_send_to_if(struct batadv_forw_packet *forw_packet,
/* send a batman ogm packet */
static void batadv_iv_ogm_emit(struct batadv_forw_packet *forw_packet)
{
- struct net_device *mesh_iface;
-
if (!forw_packet->if_incoming) {
pr_err("Error - can't forward packet: incoming iface not specified\n");
return;
}
- mesh_iface = forw_packet->if_incoming->mesh_iface;
-
if (WARN_ON(!forw_packet->if_outgoing))
return;
- if (forw_packet->if_outgoing->mesh_iface != mesh_iface) {
- pr_warn("%s: mesh interface switch for queued OGM\n", __func__);
- return;
- }
-
if (forw_packet->if_incoming->if_status != BATADV_IF_ACTIVE)
return;
@@ -910,8 +901,7 @@ out:
static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface)
{
- if (hard_iface->if_status == BATADV_IF_NOT_IN_USE ||
- hard_iface->if_status == BATADV_IF_TO_BE_REMOVED)
+ if (hard_iface->if_status == BATADV_IF_TO_BE_REMOVED)
return;
mutex_lock(&hard_iface->bat_iv.ogm_buff_mutex);
diff --git a/net/batman-adv/bat_v_elp.c b/net/batman-adv/bat_v_elp.c
index 4841f0f1a9b1..262e40040007 100644
--- a/net/batman-adv/bat_v_elp.c
+++ b/net/batman-adv/bat_v_elp.c
@@ -90,12 +90,6 @@ static bool batadv_v_elp_get_throughput(struct batadv_hardif_neigh_node *neigh,
u32 throughput;
int ret;
- /* don't query throughput when no longer associated with any
- * batman-adv interface
- */
- if (!mesh_iface)
- return false;
-
/* if the user specified a customised value for this interface, then
* return it directly
*/
@@ -311,8 +305,7 @@ static void batadv_v_elp_periodic_work(struct work_struct *work)
goto out;
/* we are in the process of shutting this interface down */
- if (hard_iface->if_status == BATADV_IF_NOT_IN_USE ||
- hard_iface->if_status == BATADV_IF_TO_BE_REMOVED)
+ if (hard_iface->if_status == BATADV_IF_TO_BE_REMOVED)
goto out;
/* the interface was enabled but may not be ready yet */
diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c
index 037921aad35d..e921d49f7ece 100644
--- a/net/batman-adv/bat_v_ogm.c
+++ b/net/batman-adv/bat_v_ogm.c
@@ -115,14 +115,14 @@ static void batadv_v_ogm_start_timer(struct batadv_priv *bat_priv)
/**
* batadv_v_ogm_send_to_if() - send a batman ogm using a given interface
- * @bat_priv: the bat priv with all the mesh interface information
* @skb: the OGM to send
* @hard_iface: the interface to use to send the OGM
*/
-static void batadv_v_ogm_send_to_if(struct batadv_priv *bat_priv,
- struct sk_buff *skb,
+static void batadv_v_ogm_send_to_if(struct sk_buff *skb,
struct batadv_hard_iface *hard_iface)
{
+ struct batadv_priv *bat_priv = netdev_priv(hard_iface->mesh_iface);
+
if (hard_iface->if_status != BATADV_IF_ACTIVE) {
kfree_skb(skb);
return;
@@ -189,7 +189,6 @@ static void batadv_v_ogm_aggr_list_free(struct batadv_hard_iface *hard_iface)
/**
* batadv_v_ogm_aggr_send() - flush & send aggregation queue
- * @bat_priv: the bat priv with all the mesh interface information
* @hard_iface: the interface with the aggregation queue to flush
*
* Aggregates all OGMv2 packets currently in the aggregation queue into a
@@ -199,8 +198,7 @@ static void batadv_v_ogm_aggr_list_free(struct batadv_hard_iface *hard_iface)
*
* Caller needs to hold the hard_iface->bat_v.aggr_list.lock.
*/
-static void batadv_v_ogm_aggr_send(struct batadv_priv *bat_priv,
- struct batadv_hard_iface *hard_iface)
+static void batadv_v_ogm_aggr_send(struct batadv_hard_iface *hard_iface)
{
unsigned int aggr_len = hard_iface->bat_v.aggr_len;
struct sk_buff *skb_aggr;
@@ -230,26 +228,21 @@ static void batadv_v_ogm_aggr_send(struct batadv_priv *bat_priv,
consume_skb(skb);
}
- batadv_v_ogm_send_to_if(bat_priv, skb_aggr, hard_iface);
+ batadv_v_ogm_send_to_if(skb_aggr, hard_iface);
}
/**
* batadv_v_ogm_queue_on_if() - queue a batman ogm on a given interface
- * @bat_priv: the bat priv with all the mesh interface information
* @skb: the OGM to queue
* @hard_iface: the interface to queue the OGM on
*/
-static void batadv_v_ogm_queue_on_if(struct batadv_priv *bat_priv,
- struct sk_buff *skb,
+static void batadv_v_ogm_queue_on_if(struct sk_buff *skb,
struct batadv_hard_iface *hard_iface)
{
- if (hard_iface->mesh_iface != bat_priv->mesh_iface) {
- kfree_skb(skb);
- return;
- }
+ struct batadv_priv *bat_priv = netdev_priv(hard_iface->mesh_iface);
if (!READ_ONCE(bat_priv->aggregated_ogms)) {
- batadv_v_ogm_send_to_if(bat_priv, skb, hard_iface);
+ batadv_v_ogm_send_to_if(skb, hard_iface);
return;
}
@@ -260,7 +253,7 @@ static void batadv_v_ogm_queue_on_if(struct batadv_priv *bat_priv,
}
if (!batadv_v_ogm_queue_left(skb, hard_iface))
- batadv_v_ogm_aggr_send(bat_priv, hard_iface);
+ batadv_v_ogm_aggr_send(hard_iface);
hard_iface->bat_v.aggr_len += batadv_v_ogm_len(skb);
__skb_queue_tail(&hard_iface->bat_v.aggr_list, skb);
@@ -357,7 +350,7 @@ static void batadv_v_ogm_send_meshif(struct batadv_priv *bat_priv)
break;
}
- batadv_v_ogm_queue_on_if(bat_priv, skb_tmp, hard_iface);
+ batadv_v_ogm_queue_on_if(skb_tmp, hard_iface);
batadv_hardif_put(hard_iface);
}
rcu_read_unlock();
@@ -397,14 +390,12 @@ void batadv_v_ogm_aggr_work(struct work_struct *work)
{
struct batadv_hard_iface_bat_v *batv;
struct batadv_hard_iface *hard_iface;
- struct batadv_priv *bat_priv;
batv = container_of(work, struct batadv_hard_iface_bat_v, aggr_wq.work);
hard_iface = container_of(batv, struct batadv_hard_iface, bat_v);
- bat_priv = netdev_priv(hard_iface->mesh_iface);
spin_lock_bh(&hard_iface->bat_v.aggr_list.lock);
- batadv_v_ogm_aggr_send(bat_priv, hard_iface);
+ batadv_v_ogm_aggr_send(hard_iface);
spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock);
batadv_v_ogm_start_queue_timer(hard_iface);
@@ -601,7 +592,7 @@ static void batadv_v_ogm_forward(struct batadv_priv *bat_priv,
if_outgoing->net_dev->name, ntohl(ogm_forward->throughput),
ogm_forward->ttl, if_incoming->net_dev->name);
- batadv_v_ogm_queue_on_if(bat_priv, skb, if_outgoing);
+ batadv_v_ogm_queue_on_if(skb, if_outgoing);
out:
batadv_orig_ifinfo_put(orig_ifinfo);
diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c
index 5c73f6ba16cf..f9a1fadf8de9 100644
--- a/net/batman-adv/bridge_loop_avoidance.c
+++ b/net/batman-adv/bridge_loop_avoidance.c
@@ -344,7 +344,6 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, const u8 *mac,
struct sk_buff *skb;
struct ethhdr *ethhdr;
struct batadv_hard_iface *primary_if;
- struct net_device *mesh_iface;
u8 *hw_src;
struct batadv_bla_claim_dst local_claim_dest;
__be32 zeroip = 0;
@@ -357,14 +356,10 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, const u8 *mac,
sizeof(local_claim_dest));
local_claim_dest.type = claimtype;
- mesh_iface = READ_ONCE(primary_if->mesh_iface);
- if (!mesh_iface)
- goto out;
-
skb = arp_create(ARPOP_REPLY, ETH_P_ARP,
/* IP DST: 0.0.0.0 */
zeroip,
- mesh_iface,
+ primary_if->mesh_iface,
/* IP SRC: 0.0.0.0 */
zeroip,
/* Ethernet DST: Broadcast */
@@ -442,7 +437,7 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, const u8 *mac,
}
skb_reset_mac_header(skb);
- skb->protocol = eth_type_trans(skb, mesh_iface);
+ skb->protocol = eth_type_trans(skb, primary_if->mesh_iface);
batadv_inc_counter(bat_priv, BATADV_CNT_RX);
batadv_add_counter(bat_priv, BATADV_CNT_RX_BYTES,
skb->len + ETH_HLEN);
diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c
index 03d01c20a954..b6867576bbaf 100644
--- a/net/batman-adv/hard-interface.c
+++ b/net/batman-adv/hard-interface.c
@@ -63,6 +63,7 @@ void batadv_hardif_release(struct kref *ref)
struct batadv_hard_iface *hard_iface;
hard_iface = container_of(ref, struct batadv_hard_iface, refcount);
+ netdev_put(hard_iface->mesh_iface, &hard_iface->meshif_dev_tracker);
netdev_put(hard_iface->net_dev, &hard_iface->dev_tracker);
kfree_rcu(hard_iface, rcu);
@@ -75,21 +76,21 @@ void batadv_hardif_release(struct kref *ref)
* Return: batadv_hard_iface of net_dev (with increased refcnt), NULL on errors
*/
struct batadv_hard_iface *
-batadv_hardif_get_by_netdev(const struct net_device *net_dev)
+batadv_hardif_get_by_netdev(struct net_device *net_dev)
{
struct batadv_hard_iface *hard_iface;
+ struct net_device *mesh_iface;
- rcu_read_lock();
- list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
- if (hard_iface->net_dev == net_dev &&
- kref_get_unless_zero(&hard_iface->refcount))
- goto out;
- }
+ ASSERT_RTNL();
- hard_iface = NULL;
+ mesh_iface = netdev_master_upper_dev_get(net_dev);
+ if (!mesh_iface || !batadv_meshif_is_valid(mesh_iface))
+ return NULL;
+
+ hard_iface = netdev_lower_dev_get_private(mesh_iface, net_dev);
+ if (!kref_get_unless_zero(&hard_iface->refcount))
+ return NULL;
-out:
- rcu_read_unlock();
return hard_iface;
}
@@ -245,7 +246,7 @@ struct net_device *__batadv_get_real_netdev(struct net_device *netdev)
}
hard_iface = batadv_hardif_get_by_netdev(netdev);
- if (!hard_iface || !hard_iface->mesh_iface)
+ if (!hard_iface)
goto out;
net = dev_net(hard_iface->mesh_iface);
@@ -539,16 +540,10 @@ static void batadv_check_known_mac_addr(const struct batadv_hard_iface *hard_ifa
const struct batadv_hard_iface *tmp_hard_iface;
struct list_head *iter;
- if (!mesh_iface)
- return;
-
netdev_for_each_lower_private(mesh_iface, tmp_hard_iface, iter) {
if (tmp_hard_iface == hard_iface)
continue;
- if (tmp_hard_iface->if_status == BATADV_IF_NOT_IN_USE)
- continue;
-
if (!batadv_compare_eth(tmp_hard_iface->net_dev->dev_addr,
hard_iface->net_dev->dev_addr))
continue;
@@ -574,9 +569,6 @@ static void batadv_hardif_recalc_extra_skbroom(struct net_device *mesh_iface)
rcu_read_lock();
netdev_for_each_lower_private_rcu(mesh_iface, hard_iface, iter) {
- if (hard_iface->if_status == BATADV_IF_NOT_IN_USE)
- continue;
-
lower_header_len = max_t(unsigned short, lower_header_len,
hard_iface->net_dev->hard_header_len);
@@ -723,38 +715,63 @@ batadv_hardif_deactivate_interface(struct batadv_hard_iface *hard_iface)
}
/**
- * batadv_hardif_enable_interface() - Enslave hard interface to mesh interface
- * @hard_iface: hard interface to add to mesh interface
+ * batadv_hardif_enable_interface() - Enslave interface to mesh interface
+ * @net_dev: netdev struct of the interface to add to mesh interface
* @mesh_iface: netdev struct of the mesh interface
*
* Return: 0 on success or negative error number in case of failure
*/
-int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
+int batadv_hardif_enable_interface(struct net_device *net_dev,
struct net_device *mesh_iface)
{
struct batadv_priv *bat_priv;
__be16 ethertype = htons(ETH_P_BATMAN);
int max_header_len = batadv_max_header_len();
+ struct batadv_hard_iface *hard_iface;
unsigned int required_mtu;
unsigned int hardif_mtu;
bool fragmentation;
int ret;
- hardif_mtu = READ_ONCE(hard_iface->net_dev->mtu);
+ ASSERT_RTNL();
+
+ if (!batadv_is_valid_iface(net_dev))
+ return -EINVAL;
+
+ hardif_mtu = READ_ONCE(net_dev->mtu);
required_mtu = READ_ONCE(mesh_iface->mtu) + max_header_len;
if (hardif_mtu < ETH_MIN_MTU + max_header_len)
return -EINVAL;
- if (hard_iface->if_status != BATADV_IF_NOT_IN_USE)
- goto out;
+ hard_iface = kzalloc_obj(*hard_iface, GFP_ATOMIC);
+ if (!hard_iface)
+ return -ENOMEM;
- kref_get(&hard_iface->refcount);
+ netdev_hold(net_dev, &hard_iface->dev_tracker, GFP_ATOMIC);
+ hard_iface->net_dev = net_dev;
+
+ hard_iface->if_status = BATADV_IF_INACTIVE;
+
+ INIT_HLIST_HEAD(&hard_iface->neigh_list);
+
+ mutex_init(&hard_iface->bat_iv.ogm_buff_mutex);
+ spin_lock_init(&hard_iface->neigh_list_lock);
+ kref_init(&hard_iface->refcount);
+
+ hard_iface->num_bcasts = BATADV_NUM_BCASTS_DEFAULT;
+ if (batadv_is_wifi_hardif(hard_iface))
+ hard_iface->num_bcasts = BATADV_NUM_BCASTS_WIRELESS;
+
+ WRITE_ONCE(hard_iface->hop_penalty, 0);
+
+ batadv_v_hardif_init(hard_iface);
netdev_hold(mesh_iface, &hard_iface->meshif_dev_tracker, GFP_ATOMIC);
hard_iface->mesh_iface = mesh_iface;
bat_priv = netdev_priv(hard_iface->mesh_iface);
+ bat_priv->hardif_generation++;
ret = netdev_master_upper_dev_link(hard_iface->net_dev,
mesh_iface, hard_iface, NULL, NULL);
if (ret)
@@ -764,9 +781,6 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
if (ret < 0)
goto err_upper;
- hard_iface->if_status = BATADV_IF_INACTIVE;
-
- kref_get(&hard_iface->refcount);
hard_iface->batman_adv_ptype.type = ethertype;
hard_iface->batman_adv_ptype.func = batadv_batman_skb_recv;
hard_iface->batman_adv_ptype.dev = hard_iface->net_dev;
@@ -802,14 +816,11 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
if (bat_priv->algo_ops->iface.enabled)
bat_priv->algo_ops->iface.enabled(hard_iface);
-out:
return 0;
err_upper:
netdev_upper_dev_unlink(hard_iface->net_dev, mesh_iface);
err_dev:
- hard_iface->mesh_iface = NULL;
- netdev_put(mesh_iface, &hard_iface->meshif_dev_tracker);
batadv_hardif_put(hard_iface);
return ret;
}
@@ -821,17 +832,18 @@ err_dev:
void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface)
{
struct batadv_priv *bat_priv = netdev_priv(hard_iface->mesh_iface);
- struct batadv_hard_iface *primary_if = NULL;
+ struct batadv_hard_iface *primary_if;
+
+ ASSERT_RTNL();
batadv_hardif_deactivate_interface(hard_iface);
if (hard_iface->if_status != BATADV_IF_INACTIVE)
- goto out;
+ return;
batadv_info(hard_iface->mesh_iface, "Removing interface: %s\n",
hard_iface->net_dev->name);
dev_remove_pack(&hard_iface->batman_adv_ptype);
- batadv_hardif_put(hard_iface);
primary_if = batadv_primary_if_get_selected(bat_priv);
if (hard_iface == primary_if) {
@@ -842,15 +854,16 @@ void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface)
batadv_hardif_put(new_if);
}
+ batadv_hardif_put(primary_if);
bat_priv->algo_ops->iface.disable(hard_iface);
- hard_iface->if_status = BATADV_IF_NOT_IN_USE;
+ hard_iface->if_status = BATADV_IF_TO_BE_REMOVED;
/* delete all references to this hard_iface */
batadv_purge_orig_ref(bat_priv);
batadv_purge_outstanding_packets(bat_priv, hard_iface);
- netdev_put(hard_iface->mesh_iface, &hard_iface->meshif_dev_tracker);
+ bat_priv->hardif_generation++;
netdev_upper_dev_unlink(hard_iface->net_dev, hard_iface->mesh_iface);
batadv_hardif_recalc_extra_skbroom(hard_iface->mesh_iface);
@@ -858,67 +871,6 @@ void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface)
if (list_empty(&hard_iface->mesh_iface->adj_list.lower))
batadv_gw_check_client_stop(bat_priv);
- hard_iface->mesh_iface = NULL;
- batadv_hardif_put(hard_iface);
-
-out:
- batadv_hardif_put(primary_if);
-}
-
-static struct batadv_hard_iface *
-batadv_hardif_add_interface(struct net_device *net_dev)
-{
- struct batadv_hard_iface *hard_iface;
-
- ASSERT_RTNL();
-
- if (!batadv_is_valid_iface(net_dev))
- return NULL;
-
- hard_iface = kzalloc_obj(*hard_iface, GFP_ATOMIC);
- if (!hard_iface)
- return NULL;
-
- netdev_hold(net_dev, &hard_iface->dev_tracker, GFP_ATOMIC);
- hard_iface->net_dev = net_dev;
-
- hard_iface->mesh_iface = NULL;
- hard_iface->if_status = BATADV_IF_NOT_IN_USE;
-
- INIT_LIST_HEAD(&hard_iface->list);
- INIT_HLIST_HEAD(&hard_iface->neigh_list);
-
- mutex_init(&hard_iface->bat_iv.ogm_buff_mutex);
- spin_lock_init(&hard_iface->neigh_list_lock);
- kref_init(&hard_iface->refcount);
-
- hard_iface->num_bcasts = BATADV_NUM_BCASTS_DEFAULT;
- if (batadv_is_wifi_hardif(hard_iface))
- hard_iface->num_bcasts = BATADV_NUM_BCASTS_WIRELESS;
-
- WRITE_ONCE(hard_iface->hop_penalty, 0);
-
- batadv_v_hardif_init(hard_iface);
-
- kref_get(&hard_iface->refcount);
- list_add_tail_rcu(&hard_iface->list, &batadv_hardif_list);
- batadv_hardif_generation++;
-
- return hard_iface;
-}
-
-static void batadv_hardif_remove_interface(struct batadv_hard_iface *hard_iface)
-{
- ASSERT_RTNL();
-
- /* first deactivate interface */
- if (hard_iface->if_status != BATADV_IF_NOT_IN_USE)
- batadv_hardif_disable_interface(hard_iface);
-
- if (hard_iface->if_status != BATADV_IF_NOT_IN_USE)
- return;
-
- hard_iface->if_status = BATADV_IF_TO_BE_REMOVED;
batadv_hardif_put(hard_iface);
}
@@ -1082,10 +1034,6 @@ static int batadv_hard_if_event(struct notifier_block *this,
batadv_wifi_net_device_event(event, net_dev);
hard_iface = batadv_hardif_get_by_netdev(net_dev);
- if (!hard_iface && (event == NETDEV_REGISTER ||
- event == NETDEV_POST_TYPE_CHANGE))
- hard_iface = batadv_hardif_add_interface(net_dev);
-
if (!hard_iface)
goto out;
@@ -1099,19 +1047,12 @@ static int batadv_hard_if_event(struct notifier_block *this,
break;
case NETDEV_UNREGISTER:
case NETDEV_PRE_TYPE_CHANGE:
- list_del_rcu(&hard_iface->list);
- batadv_hardif_generation++;
-
- batadv_hardif_remove_interface(hard_iface);
+ batadv_hardif_disable_interface(hard_iface);
break;
case NETDEV_CHANGEMTU:
- if (hard_iface->mesh_iface)
- batadv_update_min_mtu(hard_iface->mesh_iface);
+ batadv_update_min_mtu(hard_iface->mesh_iface);
break;
case NETDEV_CHANGEADDR:
- if (hard_iface->if_status == BATADV_IF_NOT_IN_USE)
- goto hardif_put;
-
batadv_check_known_mac_addr(hard_iface);
bat_priv = netdev_priv(hard_iface->mesh_iface);
diff --git a/net/batman-adv/hard-interface.h b/net/batman-adv/hard-interface.h
index af31696c3978..935f47ca9a48 100644
--- a/net/batman-adv/hard-interface.h
+++ b/net/batman-adv/hard-interface.h
@@ -22,12 +22,6 @@
*/
enum batadv_hard_if_state {
/**
- * @BATADV_IF_NOT_IN_USE: interface is not used as slave interface of a
- * batman-adv mesh interface
- */
- BATADV_IF_NOT_IN_USE,
-
- /**
* @BATADV_IF_TO_BE_REMOVED: interface will be removed from mesh
* interface
*/
@@ -74,8 +68,8 @@ u32 batadv_netdev_get_wifi_flags(struct net_device *net_dev);
u32 batadv_hardif_get_wifi_flags(struct batadv_hard_iface *hard_iface);
bool batadv_is_wifi_hardif(struct batadv_hard_iface *hard_iface);
struct batadv_hard_iface*
-batadv_hardif_get_by_netdev(const struct net_device *net_dev);
-int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
+batadv_hardif_get_by_netdev(struct net_device *net_dev);
+int batadv_hardif_enable_interface(struct net_device *net_dev,
struct net_device *mesh_iface);
void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface);
int batadv_hardif_min_mtu(struct net_device *mesh_iface);
diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c
index 4d3807a645b7..fb2b0e35e349 100644
--- a/net/batman-adv/main.c
+++ b/net/batman-adv/main.c
@@ -59,11 +59,6 @@
#include "tp_meter.h"
#include "translation-table.h"
-/* List manipulations on hardif_list have to be rtnl_lock()'ed,
- * list traversals just rcu-locked
- */
-struct list_head batadv_hardif_list;
-unsigned int batadv_hardif_generation;
static int (*batadv_rx_handler[256])(struct sk_buff *skb,
struct batadv_hard_iface *recv_if);
@@ -95,7 +90,6 @@ static int __init batadv_init(void)
if (ret < 0)
return ret;
- INIT_LIST_HEAD(&batadv_hardif_list);
batadv_algo_init();
batadv_recv_handler_init();
@@ -450,9 +444,6 @@ int batadv_batman_skb_recv(struct sk_buff *skb, struct net_device *dev,
if (unlikely(skb->mac_len != ETH_HLEN || !skb_mac_header(skb)))
goto err_free;
- if (!hard_iface->mesh_iface)
- goto err_free;
-
bat_priv = netdev_priv(hard_iface->mesh_iface);
if (READ_ONCE(bat_priv->mesh_state) != BATADV_MESH_ACTIVE)
diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h
index f68fc8b7239c..e738758ee4a7 100644
--- a/net/batman-adv/main.h
+++ b/net/batman-adv/main.h
@@ -226,9 +226,6 @@ static inline int batadv_print_vid(unsigned short vid)
return -1;
}
-extern struct list_head batadv_hardif_list;
-extern unsigned int batadv_hardif_generation;
-
extern struct workqueue_struct *batadv_event_workqueue;
int batadv_mesh_init(struct net_device *mesh_iface);
diff --git a/net/batman-adv/mesh-interface.c b/net/batman-adv/mesh-interface.c
index 511f70e0706a..5cc72bbc4570 100644
--- a/net/batman-adv/mesh-interface.c
+++ b/net/batman-adv/mesh-interface.c
@@ -837,18 +837,7 @@ static int batadv_meshif_slave_add(struct net_device *dev,
struct net_device *slave_dev,
struct netlink_ext_ack *extack)
{
- struct batadv_hard_iface *hard_iface;
- int ret = -EINVAL;
-
- hard_iface = batadv_hardif_get_by_netdev(slave_dev);
- if (!hard_iface || hard_iface->mesh_iface)
- goto out;
-
- ret = batadv_hardif_enable_interface(hard_iface, dev);
-
-out:
- batadv_hardif_put(hard_iface);
- return ret;
+ return batadv_hardif_enable_interface(slave_dev, dev);
}
/**
diff --git a/net/batman-adv/netlink.c b/net/batman-adv/netlink.c
index 4cf9e3c54ad3..d2bc48c70714 100644
--- a/net/batman-adv/netlink.c
+++ b/net/batman-adv/netlink.c
@@ -968,7 +968,7 @@ batadv_netlink_dump_hardif(struct sk_buff *msg, struct netlink_callback *cb)
bat_priv = netdev_priv(mesh_iface);
rtnl_lock();
- cb->seq = batadv_hardif_generation << 1 | 1;
+ cb->seq = bat_priv->hardif_generation << 1 | 1;
netdev_for_each_lower_private(mesh_iface, hard_iface, iter) {
if (i++ < skip)
@@ -1211,7 +1211,9 @@ batadv_netlink_get_hardif_from_ifindex(struct batadv_priv *bat_priv,
if (!hard_dev)
return ERR_PTR(-ENODEV);
+ rtnl_lock();
hard_iface = batadv_hardif_get_by_netdev(hard_dev);
+ rtnl_unlock();
if (!hard_iface)
goto err_put_harddev;
diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c
index 9b38bd9e8da7..48f837cf665a 100644
--- a/net/batman-adv/originator.c
+++ b/net/batman-adv/originator.c
@@ -1033,7 +1033,6 @@ batadv_purge_neigh_ifinfo(struct batadv_priv *bat_priv,
/* don't purge if the interface is not (going) down */
if (if_outgoing->if_status != BATADV_IF_INACTIVE &&
- if_outgoing->if_status != BATADV_IF_NOT_IN_USE &&
if_outgoing->if_status != BATADV_IF_TO_BE_REMOVED)
continue;
@@ -1077,7 +1076,6 @@ batadv_purge_orig_ifinfo(struct batadv_priv *bat_priv,
/* don't purge if the interface is not (going) down */
if (if_outgoing->if_status != BATADV_IF_INACTIVE &&
- if_outgoing->if_status != BATADV_IF_NOT_IN_USE &&
if_outgoing->if_status != BATADV_IF_TO_BE_REMOVED)
continue;
@@ -1127,10 +1125,8 @@ batadv_purge_orig_neighbors(struct batadv_priv *bat_priv,
if (batadv_has_timed_out(last_seen, BATADV_PURGE_TIMEOUT) ||
if_incoming->if_status == BATADV_IF_INACTIVE ||
- if_incoming->if_status == BATADV_IF_NOT_IN_USE ||
if_incoming->if_status == BATADV_IF_TO_BE_REMOVED) {
if (if_incoming->if_status == BATADV_IF_INACTIVE ||
- if_incoming->if_status == BATADV_IF_NOT_IN_USE ||
if_incoming->if_status == BATADV_IF_TO_BE_REMOVED)
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"neighbor purge: originator %pM, neighbor: %pM, iface: %s\n",
diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
index c2eea7dbc488..00467aa79de9 100644
--- a/net/batman-adv/tp_meter.c
+++ b/net/batman-adv/tp_meter.c
@@ -358,28 +358,16 @@ batadv_tp_list_find_sender_session(struct batadv_priv *bat_priv, const u8 *dst,
}
/**
- * batadv_tp_vars_common_release() - release batadv_tp_vars_common from lists
+ * batadv_tp_sender_release() - release batadv_tp_sender
* and queue for free after rcu grace period
- * @ref: kref pointer of the batadv_tp_vars_common
+ * @ref: kref pointer of the batadv_tp_sender
*/
-static void batadv_tp_vars_common_release(struct kref *ref)
+static void batadv_tp_sender_release(struct kref *ref)
{
- struct batadv_tp_vars_common *tp_vars;
- struct batadv_tp_unacked *un, *safe;
-
- tp_vars = container_of(ref, struct batadv_tp_vars_common, refcount);
-
- /* lock should not be needed because this object is now out of any
- * context!
- */
- spin_lock_bh(&tp_vars->unacked_lock);
- list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
- list_del(&un->list);
- kfree(un);
- }
- spin_unlock_bh(&tp_vars->unacked_lock);
+ struct batadv_tp_sender *tp_vars;
- kfree_rcu(tp_vars, rcu);
+ tp_vars = container_of(ref, struct batadv_tp_sender, common.refcount);
+ kfree_rcu(tp_vars, common.rcu);
}
/**
@@ -392,7 +380,7 @@ static void batadv_tp_sender_put(struct batadv_tp_sender *tp_vars)
if (!tp_vars)
return;
- kref_put(&tp_vars->common.refcount, batadv_tp_vars_common_release);
+ kref_put(&tp_vars->common.refcount, batadv_tp_sender_release);
}
/**
@@ -1145,9 +1133,6 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
init_waitqueue_head(&tp_vars->more_bytes);
init_completion(&tp_vars->finished);
- spin_lock_init(&tp_vars->common.unacked_lock);
- INIT_LIST_HEAD(&tp_vars->common.unacked_list);
-
spin_lock_init(&tp_vars->cc_lock);
tp_vars->prerandom_offset = 0;
@@ -1252,6 +1237,33 @@ batadv_tp_list_find_receiver_session(struct batadv_priv *bat_priv, const u8 *dst
}
/**
+ * batadv_tp_receiver_release() - release batadv_tp_receiver
+ * and queue for free after rcu grace period
+ * @ref: kref pointer of the batadv_tp_receiver
+ */
+static void batadv_tp_receiver_release(struct kref *ref)
+{
+ struct batadv_tp_receiver *tp_vars;
+ struct batadv_tp_unacked *safe;
+ struct batadv_tp_unacked *un;
+
+ tp_vars = container_of(ref, struct batadv_tp_receiver, common.refcount);
+
+ /* lock should not be needed because this object is now out of any
+ * context!
+ */
+ spin_lock_bh(&tp_vars->ack_seqno_lock);
+ list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
+ list_del(&un->list);
+ kfree(un);
+ tp_vars->unacked_count--;
+ }
+ spin_unlock_bh(&tp_vars->ack_seqno_lock);
+
+ kfree_rcu(tp_vars, common.rcu);
+}
+
+/**
* batadv_tp_receiver_put() - decrement the batadv_tp_receiver
* refcounter and possibly release it
* @tp_vars: the private data of the current TP meter session to be free'd
@@ -1261,7 +1273,7 @@ static void batadv_tp_receiver_put(struct batadv_tp_receiver *tp_vars)
if (!tp_vars)
return;
- kref_put(&tp_vars->common.refcount, batadv_tp_vars_common_release);
+ kref_put(&tp_vars->common.refcount, batadv_tp_receiver_release);
}
/**
@@ -1304,13 +1316,13 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t)
if (batadv_tp_list_detach(&tp_vars->common))
batadv_tp_receiver_put(tp_vars);
- spin_lock_bh(&tp_vars->common.unacked_lock);
- list_for_each_entry_safe(un, safe, &tp_vars->common.unacked_list, list) {
+ spin_lock_bh(&tp_vars->ack_seqno_lock);
+ list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
list_del(&un->list);
kfree(un);
- tp_vars->common.unacked_count--;
+ tp_vars->unacked_count--;
}
- spin_unlock_bh(&tp_vars->common.unacked_lock);
+ spin_unlock_bh(&tp_vars->ack_seqno_lock);
/* drop reference of timer */
if (WARN_ON(atomic_xchg(&tp_vars->receiving, 0) != 1))
@@ -1403,69 +1415,106 @@ out:
*/
static bool batadv_tp_handle_out_of_order(struct batadv_tp_receiver *tp_vars,
u32 seqno, u32 payload_len)
- __must_hold(&tp_vars->common.unacked_lock)
+ __must_hold(&tp_vars->ack_seqno_lock)
{
- struct batadv_tp_unacked *un, *new;
- bool added = false;
-
- new = kmalloc_obj(*new, GFP_ATOMIC);
- if (unlikely(!new))
- return false;
-
- new->seqno = seqno;
- new->len = payload_len;
-
- /* if the list is empty immediately attach this new object */
- if (list_empty(&tp_vars->common.unacked_list)) {
- list_add(&new->list, &tp_vars->common.unacked_list);
- tp_vars->common.unacked_count++;
- return true;
- }
-
- /* otherwise loop over the list and either drop the packet because this
- * is a duplicate or store it at the right position.
+ struct list_head *pos = &tp_vars->unacked_list;
+ struct batadv_tp_unacked *new = NULL;
+ u32 end_seqno = seqno + payload_len;
+ struct batadv_tp_unacked *safe;
+ struct batadv_tp_unacked *un;
+
+ /* loop over the list to find either an existing entry which the new
+ * seqno range can be merged with or the position at which a new entry
+ * has to be inserted.
*
* The iteration is done in the reverse way because it is likely that
* the last received packet (the one being processed now) has a bigger
* seqno than all the others already stored.
*/
- list_for_each_entry_reverse(un, &tp_vars->common.unacked_list, list) {
- /* check for duplicates */
- if (new->seqno == un->seqno) {
- if (new->len > un->len)
- un->len = new->len;
- kfree(new);
- added = true;
- break;
- }
-
- /* look for the right position */
- if (batadv_seq_before(new->seqno, un->seqno))
+ list_for_each_entry_reverse(un, &tp_vars->unacked_list, list) {
+ /* look for the right position - an un which is smaller */
+ if (batadv_seq_before(seqno, un->seqno))
continue;
- /* as soon as an entry having a bigger seqno is found, the new
- * one is attached _after_ it. In this way the list is kept in
- * ascending order
+ /* smaller/equal seqno was found but they might be directly
+ * after another or overlapping. keep only a single entry
+ *
+ * It is already known that:
+ *
+ * un->seqno <= seqno
+ *
+ * When establishing that:
+ *
+ * seqno <= un->seqno + un->len
+ *
+ * Then it is not necessary to add a new entry because the
+ * smaller/equal seqno of un might already contain the new
+ * received packet or we only add new data directly after
+ * the end of un. The latter can be identified using:
+ *
+ * un->seqno + un->len <= end_seqno
*/
- list_add(&new->list, &un->list);
- added = true;
- tp_vars->common.unacked_count++;
+ if (!batadv_seq_before(un->seqno + un->len, seqno)) {
+ /* new data directly after un? */
+ if (!batadv_seq_before(end_seqno, un->seqno + un->len))
+ un->len = end_seqno - un->seqno;
+
+ /* un now represents both old un + new range and has to
+ * be used to check if the gap to the next seqno range
+ * was closed
+ */
+ new = un;
+ } else {
+ /* as soon as an entry having a smaller seqno is found,
+ * the new one is attached _after_ it. In this way the
+ * list is kept in ascending order
+ */
+ pos = &un->list;
+ }
+
break;
}
- /* received packet with smallest seqno out of order; add it to front */
- if (!added) {
- list_add(&new->list, &tp_vars->common.unacked_list);
- tp_vars->common.unacked_count++;
+ /* no entry to merge with was found; insert a new one after the entry
+ * with the next smaller seqno (or at the front of the list when the
+ * new seqno is the smallest or the list is empty)
+ */
+ if (!new) {
+ new = kmalloc_obj(*new, GFP_ATOMIC);
+ if (unlikely(!new))
+ return false;
+
+ new->seqno = seqno;
+ new->len = payload_len;
+
+ list_add(&new->list, pos);
+ tp_vars->unacked_count++;
+ }
+
+ /* check if new filled the gap to the next list entries */
+ un = new;
+ list_for_each_entry_safe_continue(un, safe, &tp_vars->unacked_list, list) {
+ if (batadv_seq_before(end_seqno, un->seqno))
+ break;
+
+ /* next entry is overlapping or adjacent - combine both */
+ if (batadv_seq_before(end_seqno, un->seqno + un->len)) {
+ end_seqno = un->seqno + un->len;
+ new->len = end_seqno - new->seqno;
+ }
+
+ list_del(&un->list);
+ kfree(un);
+ tp_vars->unacked_count--;
}
/* remove the last (biggest) unacked seqno when list is too large */
- if (tp_vars->common.unacked_count > BATADV_TP_MAX_UNACKED) {
- un = list_last_entry(&tp_vars->common.unacked_list,
+ if (tp_vars->unacked_count > BATADV_TP_MAX_UNACKED) {
+ un = list_last_entry(&tp_vars->unacked_list,
struct batadv_tp_unacked, list);
list_del(&un->list);
kfree(un);
- tp_vars->common.unacked_count--;
+ tp_vars->unacked_count--;
}
return true;
@@ -1477,7 +1526,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_receiver *tp_vars,
* @tp_vars: the private data of the current TP meter session
*/
static void batadv_tp_ack_unordered(struct batadv_tp_receiver *tp_vars)
- __must_hold(&tp_vars->common.unacked_lock)
+ __must_hold(&tp_vars->ack_seqno_lock)
{
struct batadv_tp_unacked *un, *safe;
u32 to_ack;
@@ -1485,7 +1534,7 @@ static void batadv_tp_ack_unordered(struct batadv_tp_receiver *tp_vars)
/* go through the unacked packet list and possibly ACK them as
* well
*/
- list_for_each_entry_safe(un, safe, &tp_vars->common.unacked_list, list) {
+ list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
/* the list is ordered, therefore it is possible to stop as soon
* there is a gap between the last acked seqno and the seqno of
* the packet under inspection
@@ -1493,14 +1542,14 @@ static void batadv_tp_ack_unordered(struct batadv_tp_receiver *tp_vars)
if (batadv_seq_before(tp_vars->last_recv, un->seqno))
break;
- to_ack = un->seqno + un->len - tp_vars->last_recv;
+ to_ack = un->seqno + un->len;
- if (batadv_seq_before(tp_vars->last_recv, un->seqno + un->len))
- tp_vars->last_recv += to_ack;
+ if (batadv_seq_before(tp_vars->last_recv, to_ack))
+ tp_vars->last_recv = to_ack;
list_del(&un->list);
kfree(un);
- tp_vars->common.unacked_count--;
+ tp_vars->unacked_count--;
}
}
@@ -1547,9 +1596,9 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
tp_vars->common.bat_priv = bat_priv;
kref_init(&tp_vars->common.refcount);
- spin_lock_init(&tp_vars->common.unacked_lock);
- INIT_LIST_HEAD(&tp_vars->common.unacked_list);
- tp_vars->common.unacked_count = 0;
+ spin_lock_init(&tp_vars->ack_seqno_lock);
+ INIT_LIST_HEAD(&tp_vars->unacked_list);
+ tp_vars->unacked_count = 0;
kref_get(&tp_vars->common.refcount);
timer_setup(&tp_vars->common.timer, batadv_tp_receiver_shutdown, 0);
@@ -1609,7 +1658,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
WRITE_ONCE(tp_vars->last_recv_time, jiffies);
}
- spin_lock_bh(&tp_vars->common.unacked_lock);
+ spin_lock_bh(&tp_vars->ack_seqno_lock);
/* if the packet is a duplicate, it may be the case that an ACK has been
* lost. Resend the ACK
@@ -1625,7 +1674,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
* not been enqueued correctly
*/
if (!batadv_tp_handle_out_of_order(tp_vars, seqno, payload_len)) {
- spin_unlock_bh(&tp_vars->common.unacked_lock);
+ spin_unlock_bh(&tp_vars->ack_seqno_lock);
goto out;
}
@@ -1641,7 +1690,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
send_ack:
to_ack = tp_vars->last_recv;
- spin_unlock_bh(&tp_vars->common.unacked_lock);
+ spin_unlock_bh(&tp_vars->ack_seqno_lock);
/* send the ACK. If the received packet was out of order, the ACK that
* is going to be sent is a duplicate (the sender will count them and
diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c
index 1c9fb21985f6..49bf2ed9ecdc 100644
--- a/net/batman-adv/tvlv.c
+++ b/net/batman-adv/tvlv.c
@@ -443,6 +443,54 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv,
}
/**
+ * batadv_tvlv_hdr_next() - move a tvlv buffer cursor to the next container
+ * @tvlv_value: cursor into the tvlv buffer, advanced past the returned
+ * container's content on success
+ * @tvlv_value_len: remaining length of the tvlv buffer, reduced by the returned
+ * container's size on success
+ *
+ * Parses a single container header at the current cursor position and, if a
+ * complete container is available, advances the cursor and remaining length
+ * past it. The returned header stays valid; its content is located at
+ * (returned header + 1) and is ntohs(hdr->len) bytes long.
+ *
+ * Return: pointer to the next tvlv container header, or NULL if no further
+ * complete container is present in the buffer.
+ */
+static struct batadv_tvlv_hdr *batadv_tvlv_hdr_next(void **tvlv_value, u16 *tvlv_value_len)
+{
+ struct batadv_tvlv_hdr *tvlv_hdr;
+ u16 tvlv_value_cont_len;
+ void *tvlv_value_cont;
+ u16 tvlv_len;
+
+ tvlv_value_cont = *tvlv_value;
+ tvlv_len = *tvlv_value_len;
+
+ if (tvlv_len < sizeof(*tvlv_hdr))
+ return NULL;
+
+ tvlv_hdr = tvlv_value_cont;
+ tvlv_value_cont_len = ntohs(tvlv_hdr->len);
+ tvlv_value_cont = tvlv_hdr + 1;
+ tvlv_len -= sizeof(*tvlv_hdr);
+
+ if (tvlv_value_cont_len > tvlv_len)
+ return NULL;
+
+ /* the next tvlv header is accessed assuming (at least) 2-byte
+ * alignment, so it must start at an even offset.
+ */
+ if (tvlv_value_cont_len & 1)
+ return NULL;
+
+ *tvlv_value = (u8 *)tvlv_value_cont + tvlv_value_cont_len;
+ *tvlv_value_len = tvlv_len - tvlv_value_cont_len;
+
+ return tvlv_hdr;
+}
+
+/**
* batadv_tvlv_containers_contain() - check if a tvlv buffer holds a container
* @tvlv_value: tvlv content
* @tvlv_value_len: tvlv content length
@@ -457,28 +505,10 @@ static bool batadv_tvlv_containers_contain(void *tvlv_value,
u8 version)
{
struct batadv_tvlv_hdr *tvlv_hdr;
- u16 tvlv_value_cont_len;
-
- while (tvlv_value_len >= sizeof(*tvlv_hdr)) {
- tvlv_hdr = tvlv_value;
- tvlv_value_cont_len = ntohs(tvlv_hdr->len);
- tvlv_value = tvlv_hdr + 1;
- tvlv_value_len -= sizeof(*tvlv_hdr);
-
- if (tvlv_value_cont_len > tvlv_value_len)
- break;
-
- /* the next tvlv header is accessed assuming (at least) 2-byte
- * alignment, so it must start at an even offset.
- */
- if (tvlv_value_cont_len & 1)
- break;
+ while ((tvlv_hdr = batadv_tvlv_hdr_next(&tvlv_value, &tvlv_value_len))) {
if (tvlv_hdr->type == type && tvlv_hdr->version == version)
return true;
-
- tvlv_value = (u8 *)tvlv_value + tvlv_value_cont_len;
- tvlv_value_len -= tvlv_value_cont_len;
}
return false;
@@ -511,20 +541,8 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
u8 cifnotfound = BATADV_TVLV_HANDLER_OGM_CIFNOTFND;
int ret = NET_RX_SUCCESS;
- while (tvlv_value_len >= sizeof(*tvlv_hdr)) {
- tvlv_hdr = tvlv_value;
+ while ((tvlv_hdr = batadv_tvlv_hdr_next(&tvlv_value, &tvlv_value_len))) {
tvlv_value_cont_len = ntohs(tvlv_hdr->len);
- tvlv_value = tvlv_hdr + 1;
- tvlv_value_len -= sizeof(*tvlv_hdr);
-
- if (tvlv_value_cont_len > tvlv_value_len)
- break;
-
- /* the next tvlv header is accessed assuming (at least) 2-byte
- * alignment, so it must start at an even offset.
- */
- if (tvlv_value_cont_len & 1)
- break;
tvlv_handler = batadv_tvlv_handler_get(bat_priv,
tvlv_hdr->type,
@@ -532,11 +550,9 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
ret |= batadv_tvlv_call_handler(bat_priv, tvlv_handler,
packet_type, orig_node, skb,
- tvlv_value,
+ tvlv_hdr + 1,
tvlv_value_cont_len);
batadv_tvlv_handler_put(tvlv_handler);
- tvlv_value = (u8 *)tvlv_value + tvlv_value_cont_len;
- tvlv_value_len -= tvlv_value_cont_len;
}
if (packet_type != BATADV_IV_OGM &&
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
index b1f9f8964c3f..cd12755d21f3 100644
--- a/net/batman-adv/types.h
+++ b/net/batman-adv/types.h
@@ -214,9 +214,6 @@ struct batadv_wifi_net_device_state {
* struct batadv_hard_iface - network device known to batman-adv
*/
struct batadv_hard_iface {
- /** @list: list node for batadv_hardif_list */
- struct list_head list;
-
/** @if_status: status of the interface for batman-adv */
char if_status;
@@ -1335,9 +1332,9 @@ struct batadv_tp_unacked {
u32 seqno;
/** @len: length of the packet */
- u16 len;
+ u32 len;
- /** @list: list node for &batadv_tp_vars_common.unacked_list */
+ /** @list: list node for &batadv_tp_receiver.unacked_list */
struct list_head list;
};
@@ -1360,15 +1357,6 @@ struct batadv_tp_vars_common {
/** @session: TP session identifier */
u8 session[2];
- /** @unacked_list: list of unacked packets (meta-info only) */
- struct list_head unacked_list;
-
- /** @unacked_lock: protect unacked_list + &batadv_tp_receiver.last_recv */
- spinlock_t unacked_lock;
-
- /** @unacked_count: number of unacked entries */
- size_t unacked_count;
-
/** @refcount: number of context where the object is used */
struct kref refcount;
@@ -1482,6 +1470,15 @@ struct batadv_tp_receiver {
/** @last_recv_time: time (jiffies) a msg was received */
unsigned long last_recv_time;
+
+ /** @unacked_list: list of unacked packets (meta-info only) */
+ struct list_head unacked_list;
+
+ /** @ack_seqno_lock: protect unacked_list + &batadv_tp_receiver.last_recv */
+ spinlock_t ack_seqno_lock;
+
+ /** @unacked_count: number of unacked entries */
+ size_t unacked_count;
};
/**
@@ -1679,6 +1676,9 @@ struct batadv_priv {
/** @tp_num: number of currently active tp sessions */
atomic_t tp_num;
+ /** @hardif_generation: generation counter added to netlink hardif dumps */
+ unsigned int hardif_generation;
+
/** @orig_work: work queue callback item for orig node purging */
struct delayed_work orig_work;
diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c
index f20c039e44c8..042d31278713 100644
--- a/net/bridge/netfilter/ebtables.c
+++ b/net/bridge/netfilter/ebtables.c
@@ -39,6 +39,8 @@
#define COUNTER_OFFSET(n) (SMP_ALIGN(n * sizeof(struct ebt_counter)))
#define COUNTER_BASE(c, n, cpu) ((struct ebt_counter *)(((char *)c) + \
COUNTER_OFFSET(n) * cpu))
+#define MAX_EBT_ENTRIES (((INT_MAX - sizeof(struct ebt_table_info)) / \
+ NR_CPUS - SMP_CACHE_BYTES) / sizeof(struct ebt_counter))
struct ebt_pernet {
struct list_head tables;
@@ -1124,10 +1126,9 @@ static int do_replace(struct net *net, sockptr_t arg, unsigned int len)
return -EINVAL;
/* overflow check */
- if (tmp.nentries >= ((INT_MAX - sizeof(struct ebt_table_info)) /
- NR_CPUS - SMP_CACHE_BYTES) / sizeof(struct ebt_counter))
+ if (tmp.nentries >= MAX_EBT_ENTRIES)
return -ENOMEM;
- if (tmp.num_counters >= INT_MAX / sizeof(struct ebt_counter))
+ if (tmp.num_counters >= MAX_EBT_ENTRIES)
return -ENOMEM;
tmp.name[sizeof(tmp.name) - 1] = 0;
@@ -2265,10 +2266,9 @@ static int compat_copy_ebt_replace_from_user(struct ebt_replace *repl,
if (tmp.entries_size == 0)
return -EINVAL;
- if (tmp.nentries >= ((INT_MAX - sizeof(struct ebt_table_info)) /
- NR_CPUS - SMP_CACHE_BYTES) / sizeof(struct ebt_counter))
+ if (tmp.nentries >= MAX_EBT_ENTRIES)
return -ENOMEM;
- if (tmp.num_counters >= INT_MAX / sizeof(struct ebt_counter))
+ if (tmp.num_counters >= MAX_EBT_ENTRIES)
return -ENOMEM;
memcpy(repl, &tmp, offsetof(struct ebt_replace, hook_entry));
diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c
index cf374c208732..22e5e5e1a9c4 100644
--- a/net/core/fib_rules.c
+++ b/net/core/fib_rules.c
@@ -51,7 +51,6 @@ bool fib_rule_matchall(const struct fib_rule *rule)
return false;
return true;
}
-EXPORT_SYMBOL_GPL(fib_rule_matchall);
int fib_default_rule_add(struct fib_rules_ops *ops,
u32 pref, u32 table)
@@ -78,7 +77,6 @@ int fib_default_rule_add(struct fib_rules_ops *ops,
list_add_tail(&r->list, &ops->rules_list);
return 0;
}
-EXPORT_SYMBOL(fib_default_rule_add);
static u32 fib_default_rule_pref(struct fib_rules_ops *ops)
{
@@ -172,6 +170,7 @@ fib_rules_register(const struct fib_rules_ops *tmpl, struct net *net)
return ERR_PTR(-ENOMEM);
INIT_LIST_HEAD(&ops->rules_list);
+ mutex_init(&ops->lock);
ops->fro_net = net;
err = __fib_rules_register(ops);
@@ -182,7 +181,6 @@ fib_rules_register(const struct fib_rules_ops *tmpl, struct net *net)
return ops;
}
-EXPORT_SYMBOL_GPL(fib_rules_register);
static void fib_rules_cleanup_ops(struct fib_rules_ops *ops)
{
@@ -207,7 +205,6 @@ void fib_rules_unregister(struct fib_rules_ops *ops)
fib_rules_cleanup_ops(ops);
kfree_rcu(ops, rcu);
}
-EXPORT_SYMBOL_GPL(fib_rules_unregister);
static int uid_range_set(struct fib_kuid_range *range)
{
@@ -363,7 +360,6 @@ out:
return err;
}
-EXPORT_SYMBOL_GPL(fib_rules_lookup);
static int call_fib_rule_notifier(struct notifier_block *nb,
enum fib_event_type event_type,
@@ -391,7 +387,7 @@ static int call_fib_rule_notifiers(struct net *net,
.rule = rule,
};
- ASSERT_RTNL_NET(net);
+ lockdep_assert_held(&ops->lock);
/* Paired with READ_ONCE() in fib_rules_seq() */
WRITE_ONCE(ops->fib_rules_seq, ops->fib_rules_seq + 1);
@@ -423,7 +419,6 @@ int fib_rules_dump(struct net *net, struct notifier_block *nb, int family,
return err;
}
-EXPORT_SYMBOL_GPL(fib_rules_dump);
unsigned int fib_rules_seq_read(const struct net *net, int family)
{
@@ -439,7 +434,6 @@ unsigned int fib_rules_seq_read(const struct net *net, int family)
return fib_rules_seq;
}
-EXPORT_SYMBOL_GPL(fib_rules_seq_read);
static struct fib_rule *rule_find(struct fib_rules_ops *ops,
struct fib_rule_hdr *frh,
@@ -740,10 +734,10 @@ errout:
return err;
}
-static int fib_nl2rule_rtnl(struct fib_rule *nlrule,
- struct fib_rules_ops *ops,
- struct nlattr *tb[],
- struct netlink_ext_ack *extack)
+static int fib_nl2rule_locked(struct fib_rule *nlrule,
+ struct fib_rules_ops *ops,
+ struct nlattr *tb[],
+ struct netlink_ext_ack *extack)
{
if (!tb[FRA_PRIORITY])
nlrule->pref = fib_default_rule_pref(ops);
@@ -754,12 +748,14 @@ static int fib_nl2rule_rtnl(struct fib_rule *nlrule,
return -EINVAL;
}
+ rcu_read_lock();
+
if (tb[FRA_IIFNAME]) {
struct net_device *dev;
- dev = __dev_get_by_name(nlrule->fr_net, nlrule->iifname);
+ dev = dev_get_by_name_rcu(nlrule->fr_net, nlrule->iifname);
if (dev) {
- nlrule->iifindex = dev->ifindex;
+ nlrule->iifindex = READ_ONCE(dev->ifindex);
nlrule->iif_is_l3_master = netif_is_l3_master(dev);
}
}
@@ -767,13 +763,15 @@ static int fib_nl2rule_rtnl(struct fib_rule *nlrule,
if (tb[FRA_OIFNAME]) {
struct net_device *dev;
- dev = __dev_get_by_name(nlrule->fr_net, nlrule->oifname);
+ dev = dev_get_by_name_rcu(nlrule->fr_net, nlrule->oifname);
if (dev) {
- nlrule->oifindex = dev->ifindex;
+ nlrule->oifindex = READ_ONCE(dev->ifindex);
nlrule->oif_is_l3_master = netif_is_l3_master(dev);
}
}
+ rcu_read_unlock();
+
return 0;
}
@@ -883,6 +881,7 @@ int fib_newrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr *tb[FRA_MAX + 1];
bool user_priority = false;
struct fib_rule_hdr *frh;
+ bool unlock_rtnl = false;
frh = nlmsg_payload(nlh, sizeof(*frh));
if (!frh) {
@@ -908,10 +907,13 @@ int fib_newrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh,
if (err)
goto errout;
- if (!rtnl_held)
+ if (!rtnl_held && ops->need_rtnl && ops->need_rtnl(net)) {
+ unlock_rtnl = true;
rtnl_net_lock(net);
+ }
+ mutex_lock(&ops->lock);
- err = fib_nl2rule_rtnl(rule, ops, tb, extack);
+ err = fib_nl2rule_locked(rule, ops, tb, extack);
if (err)
goto errout_free;
@@ -959,7 +961,7 @@ int fib_newrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh,
list_for_each_entry(r, &ops->rules_list, list) {
if (r->action == FR_ACT_GOTO &&
r->target == rule->pref &&
- rtnl_dereference(r->ctarget) == NULL) {
+ !rcu_access_pointer(r->ctarget)) {
rcu_assign_pointer(r->ctarget, rule);
if (--ops->unresolved_rules == 0)
break;
@@ -978,7 +980,8 @@ int fib_newrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh,
fib_rule_get(rule);
- if (!rtnl_held)
+ mutex_unlock(&ops->lock);
+ if (unlock_rtnl)
rtnl_net_unlock(net);
notify_rule_change(RTM_NEWRULE, rule, ops, nlh, NETLINK_CB(skb).portid);
@@ -988,7 +991,8 @@ int fib_newrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh,
return 0;
errout_free:
- if (!rtnl_held)
+ mutex_unlock(&ops->lock);
+ if (unlock_rtnl)
rtnl_net_unlock(net);
kfree(rule);
errout:
@@ -1037,10 +1041,9 @@ int fib_delrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh,
if (err)
goto errout;
- if (!rtnl_held)
- rtnl_net_lock(net);
+ mutex_lock(&ops->lock);
- err = fib_nl2rule_rtnl(nlrule, ops, tb, extack);
+ err = fib_nl2rule_locked(nlrule, ops, tb, extack);
if (err)
goto errout_free;
@@ -1055,11 +1058,8 @@ int fib_delrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh,
goto errout_free;
}
- if (ops->delete) {
- err = ops->delete(rule);
- if (err)
- goto errout_free;
- }
+ if (ops->delete)
+ ops->delete(rule);
if (rule->tun_id)
ip_tunnel_unneed_metadata();
@@ -1068,7 +1068,7 @@ int fib_delrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh,
if (rule->action == FR_ACT_GOTO) {
ops->nr_goto_rules--;
- if (rtnl_dereference(rule->ctarget) == NULL)
+ if (!rcu_access_pointer(rule->ctarget))
ops->unresolved_rules--;
}
@@ -1086,7 +1086,7 @@ int fib_delrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh,
if (&n->list == &ops->rules_list || n->pref != rule->pref)
n = NULL;
list_for_each_entry(r, &ops->rules_list, list) {
- if (rtnl_dereference(r->ctarget) != rule)
+ if (rcu_access_pointer(r->ctarget) != rule)
continue;
rcu_assign_pointer(r->ctarget, n);
if (!n)
@@ -1096,8 +1096,7 @@ int fib_delrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh,
call_fib_rule_notifiers(net, FIB_EVENT_RULE_DEL, rule, ops, NULL);
- if (!rtnl_held)
- rtnl_net_unlock(net);
+ mutex_unlock(&ops->lock);
notify_rule_change(RTM_DELRULE, rule, ops, nlh, NETLINK_CB(skb).portid);
fib_rule_put(rule);
@@ -1107,8 +1106,7 @@ int fib_delrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh,
return 0;
errout_free:
- if (!rtnl_held)
- rtnl_net_unlock(net);
+ mutex_unlock(&ops->lock);
kfree(nlrule);
errout:
rules_ops_put(ops);
@@ -1402,24 +1400,30 @@ static int fib_rules_event(struct notifier_block *this, unsigned long event,
struct net *net = dev_net(dev);
struct fib_rules_ops *ops;
- ASSERT_RTNL();
-
switch (event) {
case NETDEV_REGISTER:
- list_for_each_entry(ops, &net->rules_ops, list)
+ list_for_each_entry(ops, &net->rules_ops, list) {
+ mutex_lock(&ops->lock);
attach_rules(&ops->rules_list, dev);
+ mutex_unlock(&ops->lock);
+ }
break;
case NETDEV_CHANGENAME:
list_for_each_entry(ops, &net->rules_ops, list) {
+ mutex_lock(&ops->lock);
detach_rules(&ops->rules_list, dev);
attach_rules(&ops->rules_list, dev);
+ mutex_unlock(&ops->lock);
}
break;
case NETDEV_UNREGISTER:
- list_for_each_entry(ops, &net->rules_ops, list)
+ list_for_each_entry(ops, &net->rules_ops, list) {
+ mutex_lock(&ops->lock);
detach_rules(&ops->rules_list, dev);
+ mutex_unlock(&ops->lock);
+ }
break;
}
diff --git a/net/core/netpoll.c b/net/core/netpoll.c
index 96d5945e6a30..aed415d3cd74 100644
--- a/net/core/netpoll.c
+++ b/net/core/netpoll.c
@@ -221,7 +221,7 @@ static void refill_skbs(struct netpoll *np)
skb_pool = &np->skb_pool;
while (READ_ONCE(skb_pool->qlen) < MAX_SKBS) {
- skb = alloc_skb(MAX_SKB_SIZE, GFP_ATOMIC);
+ skb = alloc_skb(MAX_SKB_SIZE, GFP_ATOMIC | __GFP_NOWARN);
if (!skb)
break;
diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c
index 42212970d735..a5e739d32d59 100644
--- a/net/ipv4/fib_frontend.c
+++ b/net/ipv4/fib_frontend.c
@@ -76,7 +76,7 @@ fail:
struct fib_table *fib_new_table(struct net *net, u32 id)
{
- struct fib_table *tb, *alias = NULL;
+ struct fib_table *tb, *new_tb, *alias = NULL;
unsigned int h;
if (id == 0)
@@ -85,14 +85,27 @@ struct fib_table *fib_new_table(struct net *net, u32 id)
if (tb)
return tb;
+ if (!check_net(net))
+ return NULL;
+
if (id == RT_TABLE_LOCAL && !net->ipv4.fib_has_custom_rules)
alias = fib_new_table(net, RT_TABLE_MAIN);
- if (check_net(net))
- tb = fib_trie_table(id, alias);
- if (!tb)
+ new_tb = fib_trie_table(id, alias);
+ if (!new_tb)
return NULL;
+ spin_lock(&net->ipv4.fib_table_hash_lock);
+
+ tb = fib_get_table(net, id);
+ if (tb) {
+ spin_unlock(&net->ipv4.fib_table_hash_lock);
+ fib_free_table(new_tb);
+ return tb;
+ }
+
+ tb = new_tb;
+
switch (id) {
case RT_TABLE_MAIN:
rcu_assign_pointer(net->ipv4.fib_main, tb);
@@ -106,28 +119,35 @@ struct fib_table *fib_new_table(struct net *net, u32 id)
h = id & (FIB_TABLE_HASHSZ - 1);
hlist_add_head_rcu(&tb->tb_hlist, &net->ipv4.fib_table_hash[h]);
+
+ spin_unlock(&net->ipv4.fib_table_hash_lock);
+
return tb;
}
EXPORT_SYMBOL_GPL(fib_new_table);
-/* caller must hold either rtnl or rcu read lock */
struct fib_table *fib_get_table(struct net *net, u32 id)
{
- struct fib_table *tb;
+ struct fib_table *tb = NULL;
struct hlist_head *head;
unsigned int h;
if (id == 0)
id = RT_TABLE_MAIN;
h = id & (FIB_TABLE_HASHSZ - 1);
-
head = &net->ipv4.fib_table_hash[h];
- hlist_for_each_entry_rcu(tb, head, tb_hlist,
- lockdep_rtnl_is_held()) {
+
+ /* fib_table is not destroyed until ip_fib_net_exit()
+ * except for the merged main/local table.
+ * fib_unmerge() is called under RTNL, so other readers
+ * under RTNL (e.g. fib_flush(), fib_info_notify_update())
+ * can safely traverse the list with rcu_dereference_raw().
+ */
+ hlist_for_each_entry_rcu(tb, head, tb_hlist, true)
if (tb->tb_id == id)
- return tb;
- }
- return NULL;
+ break;
+
+ return tb;
}
#endif /* CONFIG_IP_MULTIPLE_TABLES */
@@ -190,10 +210,9 @@ void fib_flush(struct net *net)
for (h = 0; h < FIB_TABLE_HASHSZ; h++) {
struct hlist_head *head = &net->ipv4.fib_table_hash[h];
- struct hlist_node *tmp;
struct fib_table *tb;
- hlist_for_each_entry_safe(tb, tmp, head, tb_hlist)
+ hlist_for_each_entry_rcu(tb, head, tb_hlist, true)
flushed += fib_table_flush(net, tb, false);
}
@@ -1019,10 +1038,11 @@ static int inet_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
.dump_routes = true,
.dump_exceptions = true,
};
- unsigned int e = 0, s_e, h, s_h;
struct hlist_head *head;
int dumped = 0, err = 0;
struct fib_table *tb;
+ unsigned int h, s_h;
+ u32 s_id;
rcu_read_lock();
if (cb->strict_check) {
@@ -1054,29 +1074,28 @@ static int inet_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
}
s_h = cb->args[0];
- s_e = cb->args[1];
+ s_id = cb->args[1];
err = 0;
- for (h = s_h; h < FIB_TABLE_HASHSZ; h++, s_e = 0) {
- e = 0;
+ for (h = s_h; h < FIB_TABLE_HASHSZ; h++, s_id = 0) {
head = &net->ipv4.fib_table_hash[h];
hlist_for_each_entry_rcu(tb, head, tb_hlist) {
- if (e < s_e)
- goto next;
+ if (s_id && tb->tb_id != s_id)
+ continue;
+
+ s_id = 0;
if (dumped)
memset(&cb->args[2], 0, sizeof(cb->args) -
2 * sizeof(cb->args[0]));
+ cb->args[1] = tb->tb_id;
err = fib_table_dump(tb, skb, cb, &filter);
if (err < 0)
goto out;
dumped = 1;
-next:
- e++;
}
}
out:
- cb->args[1] = e;
cb->args[0] = h;
unlock:
@@ -1565,6 +1584,7 @@ static int __net_init ip_fib_net_init(struct net *net)
net->ipv4.sysctl_fib_multipath_hash_fields =
FIB_MULTIPATH_HASH_FIELD_DEFAULT_MASK;
#endif
+ spin_lock_init(&net->ipv4.fib_table_hash_lock);
/* Avoid false sharing : Use at least a full cache line */
size = max_t(size_t, size, L1_CACHE_BYTES);
diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c
index e068a5bace73..4edb0dca7be8 100644
--- a/net/ipv4/fib_rules.c
+++ b/net/ipv4/fib_rules.c
@@ -301,10 +301,12 @@ static int fib4_rule_configure(struct fib_rule *rule, struct sk_buff *skb,
fib4_nl2rule_dscp_mask(tb[FRA_DSCP_MASK], rule4, extack) < 0)
goto errout;
- /* split local/main if they are not already split */
- err = fib_unmerge(net);
- if (err)
- goto errout;
+ if (!net->ipv4.fib_has_custom_rules) {
+ /* split local/main if they are not already split */
+ err = fib_unmerge(net);
+ if (err)
+ goto errout;
+ }
if (rule->table == RT_TABLE_UNSPEC && !rule->l3mdev) {
if (rule->action == FR_ACT_TO_TBL) {
@@ -349,7 +351,7 @@ errout:
return err;
}
-static int fib4_rule_delete(struct fib_rule *rule)
+static void fib4_rule_delete(struct fib_rule *rule)
{
struct net *net = rule->fr_net;
@@ -361,8 +363,6 @@ static int fib4_rule_delete(struct fib_rule *rule)
if (net->ipv4.fib_rules_require_fldissect &&
fib_rule_requires_fldissect(rule))
net->ipv4.fib_rules_require_fldissect--;
-
- return 0;
}
static int fib4_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh,
@@ -460,6 +460,11 @@ static void fib4_rule_flush_cache(struct fib_rules_ops *ops)
rt_cache_flush(ops->fro_net);
}
+static bool fib4_rule_need_rtnl(struct net *net)
+{
+ return !net->ipv4.fib_has_custom_rules;
+}
+
static const struct fib_rules_ops __net_initconst fib4_rules_ops_template = {
.family = AF_INET,
.rule_size = sizeof(struct fib4_rule),
@@ -473,6 +478,7 @@ static const struct fib_rules_ops __net_initconst fib4_rules_ops_template = {
.fill = fib4_rule_fill,
.nlmsg_payload = fib4_rule_nlmsg_payload,
.flush_cache = fib4_rule_flush_cache,
+ .need_rtnl = fib4_rule_need_rtnl,
.nlgroup = RTNLGRP_IPV4_RULE,
.owner = THIS_MODULE,
};
diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c
index e11dc86ceda0..d1d342d7148e 100644
--- a/net/ipv4/fib_trie.c
+++ b/net/ipv4/fib_trie.c
@@ -2137,8 +2137,7 @@ void fib_info_notify_update(struct net *net, struct nl_info *info)
struct hlist_head *head = &net->ipv4.fib_table_hash[h];
struct fib_table *tb;
- hlist_for_each_entry_rcu(tb, head, tb_hlist,
- lockdep_rtnl_is_held())
+ hlist_for_each_entry_rcu(tb, head, tb_hlist, true)
__fib_info_notify_update(net, tb, info);
}
}
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index b6337a47c141..116ce7cec80e 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -1473,6 +1473,7 @@ int inet_fill_ifmcaddr(struct sk_buff *skb, struct net_device *dev,
ci.ifa_valid = INFINITY_LIFE_TIME;
if (nla_put_in_addr(skb, IFA_MULTICAST, im->multiaddr) < 0 ||
+ nla_put_u32(skb, IFA_MC_USERS, READ_ONCE(im->users)) < 0 ||
nla_put(skb, IFA_CACHEINFO, sizeof(ci), &ci) < 0) {
nlmsg_cancel(skb, nlh);
return -EMSGSIZE;
@@ -1494,6 +1495,7 @@ static void inet_ifmcaddr_notify(struct net_device *dev,
skb = nlmsg_new(NLMSG_ALIGN(sizeof(struct ifaddrmsg)) +
nla_total_size(sizeof(__be32)) +
+ nla_total_size(sizeof(u32)) +
nla_total_size(sizeof(struct ifa_cacheinfo)),
GFP_KERNEL);
if (!skb)
diff --git a/net/ipv4/netfilter/nf_nat_snmp_basic_main.c b/net/ipv4/netfilter/nf_nat_snmp_basic_main.c
index 0ede138dfd29..e540b86bd15b 100644
--- a/net/ipv4/netfilter/nf_nat_snmp_basic_main.c
+++ b/net/ipv4/netfilter/nf_nat_snmp_basic_main.c
@@ -213,7 +213,7 @@ static int __init nf_nat_snmp_basic_init(void)
RCU_INIT_POINTER(nf_nat_snmp_hook, help);
nf_ct_helper_init(&snmp_trap_helper, AF_INET, IPPROTO_UDP,
- "snmp_trap", SNMP_TRAP_PORT, SNMP_TRAP_PORT, SNMP_TRAP_PORT,
+ "snmp_trap",
&snmp_exp_policy, 0, help, NULL, THIS_MODULE);
err = nf_conntrack_helper_register(&snmp_trap_helper, &snmp_trap_helper_ptr);
diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c
index e9fbab6ad914..2aebaf8297e0 100644
--- a/net/ipv4/raw.c
+++ b/net/ipv4/raw.c
@@ -809,23 +809,18 @@ static int raw_seticmpfilter(struct sock *sk, sockptr_t optval, int optlen)
return 0;
}
-static int raw_geticmpfilter(struct sock *sk, char __user *optval, int __user *optlen)
+static int raw_geticmpfilter(struct sock *sk, sockopt_t *opt)
{
- int len, ret = -EFAULT;
+ int len = opt->optlen;
- if (get_user(len, optlen))
- goto out;
- ret = -EINVAL;
if (len < 0)
- goto out;
+ return -EINVAL;
if (len > sizeof(struct icmp_filter))
len = sizeof(struct icmp_filter);
- ret = -EFAULT;
- if (put_user(len, optlen) ||
- copy_to_user(optval, &raw_sk(sk)->filter, len))
- goto out;
- ret = 0;
-out: return ret;
+ opt->optlen = len;
+ if (copy_to_iter(&raw_sk(sk)->filter, len, &opt->iter_out) != len)
+ return -EFAULT;
+ return 0;
}
static int do_raw_setsockopt(struct sock *sk, int optname,
@@ -848,14 +843,13 @@ static int raw_setsockopt(struct sock *sk, int level, int optname,
return do_raw_setsockopt(sk, optname, optval, optlen);
}
-static int do_raw_getsockopt(struct sock *sk, int optname,
- char __user *optval, int __user *optlen)
+static int do_raw_getsockopt(struct sock *sk, int optname, sockopt_t *opt)
{
if (optname == ICMP_FILTER) {
if (inet_sk(sk)->inet_num != IPPROTO_ICMP)
return -EOPNOTSUPP;
else
- return raw_geticmpfilter(sk, optval, optlen);
+ return raw_geticmpfilter(sk, opt);
}
return -ENOPROTOOPT;
}
@@ -863,9 +857,24 @@ static int do_raw_getsockopt(struct sock *sk, int optname,
static int raw_getsockopt(struct sock *sk, int level, int optname,
char __user *optval, int __user *optlen)
{
+ sockopt_t opt;
+ int err;
+
if (level != SOL_RAW)
return ip_getsockopt(sk, level, optname, optval, optlen);
- return do_raw_getsockopt(sk, optname, optval, optlen);
+
+ err = sockopt_init_user(&opt, optval, optlen);
+ if (err)
+ return err;
+
+ err = do_raw_getsockopt(sk, optname, &opt);
+ if (err)
+ return err;
+
+ if (put_user(opt.optlen, optlen))
+ return -EFAULT;
+
+ return 0;
}
static int raw_ioctl(struct sock *sk, int cmd, int *karg)
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 70f6cbd4ef73..59248a59358c 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -76,6 +76,7 @@
#include <linux/bpf-cgroup.h>
#include <linux/uaccess.h>
+#include <linux/uio.h>
#include <asm/ioctls.h>
#include <linux/memblock.h>
#include <linux/highmem.h>
@@ -2995,14 +2996,13 @@ static int udp_setsockopt(struct sock *sk, int level, int optname, sockptr_t opt
}
int udp_lib_getsockopt(struct sock *sk, int level, int optname,
- char __user *optval, int __user *optlen)
+ sockopt_t *opt)
{
struct udp_sock *up = udp_sk(sk);
int val, len;
- if (get_user(len, optlen))
- return -EFAULT;
-
+ len = opt->optlen;
+ /* keep the check so direct sockopt_t callers stay covered. */
if (len < 0)
return -EINVAL;
@@ -3037,9 +3037,8 @@ int udp_lib_getsockopt(struct sock *sk, int level, int optname,
return -ENOPROTOOPT;
}
- if (put_user(len, optlen))
- return -EFAULT;
- if (copy_to_user(optval, &val, len))
+ opt->optlen = len;
+ if (copy_to_iter(&val, len, &opt->iter_out) != len)
return -EFAULT;
return 0;
}
@@ -3047,9 +3046,29 @@ int udp_lib_getsockopt(struct sock *sk, int level, int optname,
static int udp_getsockopt(struct sock *sk, int level, int optname,
char __user *optval, int __user *optlen)
{
- if (level == SOL_UDP)
- return udp_lib_getsockopt(sk, level, optname, optval, optlen);
- return ip_getsockopt(sk, level, optname, optval, optlen);
+ sockopt_t opt;
+ int err;
+
+ /*
+ * keep the old __user pointers, until ip_getsockopt() moves
+ * to sockopt_t
+ */
+ if (level != SOL_UDP)
+ return ip_getsockopt(sk, level, optname, optval, optlen);
+
+ err = sockopt_init_user(&opt, optval, optlen);
+ if (err)
+ return err;
+
+ err = udp_lib_getsockopt(sk, level, optname, &opt);
+ if (err)
+ return err;
+
+ /* optval was written by copy_to_iter() in udp_lib_getsockopt() */
+ if (put_user(opt.optlen, optlen))
+ return -EFAULT;
+
+ return 0;
}
/**
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index cbe681de3818..f1fe9ede1edb 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -5264,6 +5264,7 @@ int inet6_fill_ifmcaddr(struct sk_buff *skb,
put_ifaddrmsg(nlh, 128, IFA_F_PERMANENT, scope, ifindex);
if (nla_put_in6_addr(skb, IFA_MULTICAST, &ifmca->mca_addr) < 0 ||
+ nla_put_u32(skb, IFA_MC_USERS, READ_ONCE(ifmca->mca_users)) < 0 ||
put_cacheinfo(skb, ifmca->mca_cstamp, READ_ONCE(ifmca->mca_tstamp),
INFINITY_LIFE_TIME, INFINITY_LIFE_TIME) < 0) {
nlmsg_cancel(skb, nlh);
diff --git a/net/ipv6/fib6_rules.c b/net/ipv6/fib6_rules.c
index e1b2b4fa6e18..04dab9329d0c 100644
--- a/net/ipv6/fib6_rules.c
+++ b/net/ipv6/fib6_rules.c
@@ -480,15 +480,13 @@ errout:
return err;
}
-static int fib6_rule_delete(struct fib_rule *rule)
+static void fib6_rule_delete(struct fib_rule *rule)
{
struct net *net = rule->fr_net;
if (net->ipv6.fib6_rules_require_fldissect &&
fib_rule_requires_fldissect(rule))
net->ipv6.fib6_rules_require_fldissect--;
-
- return 0;
}
static int fib6_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh,
@@ -637,21 +635,14 @@ out_fib6_rules_ops:
goto out;
}
-static void __net_exit fib6_rules_net_exit_batch(struct list_head *net_list)
+static void __net_exit fib6_rules_net_exit(struct net *net)
{
- struct net *net;
-
- rtnl_lock();
- list_for_each_entry(net, net_list, exit_list) {
- fib_rules_unregister(net->ipv6.fib6_rules_ops);
- cond_resched();
- }
- rtnl_unlock();
+ fib_rules_unregister(net->ipv6.fib6_rules_ops);
}
static struct pernet_operations fib6_rules_net_ops = {
.init = fib6_rules_net_init,
- .exit_batch = fib6_rules_net_exit_batch,
+ .exit = fib6_rules_net_exit,
};
int __init fib6_rules_init(void)
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index 04b811b3be97..774f4c72a6fa 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -908,6 +908,7 @@ static void inet6_ifmcaddr_notify(struct net_device *dev,
skb = nlmsg_new(NLMSG_ALIGN(sizeof(struct ifaddrmsg)) +
nla_total_size(sizeof(struct in6_addr)) +
+ nla_total_size(sizeof(u32)) +
nla_total_size(sizeof(struct ifa_cacheinfo)),
GFP_KERNEL);
if (!skb)
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 15e032194ecc..392e18b97045 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -1826,9 +1826,22 @@ static int udpv6_setsockopt(struct sock *sk, int level, int optname,
static int udpv6_getsockopt(struct sock *sk, int level, int optname,
char __user *optval, int __user *optlen)
{
- if (level == SOL_UDP)
- return udp_lib_getsockopt(sk, level, optname, optval, optlen);
- return ipv6_getsockopt(sk, level, optname, optval, optlen);
+ sockopt_t opt;
+ int err;
+
+ if (level != SOL_UDP)
+ return ipv6_getsockopt(sk, level, optname, optval, optlen);
+
+ err = sockopt_init_user(&opt, optval, optlen);
+ if (err)
+ return err;
+
+ err = udp_lib_getsockopt(sk, level, optname, &opt);
+ if (err)
+ return err;
+ if (put_user(opt.optlen, optlen))
+ return -EFAULT;
+ return 0;
}
diff --git a/net/netfilter/ipvs/ip_vs_nfct.c b/net/netfilter/ipvs/ip_vs_nfct.c
index 81974f69e5bb..347185fd0c8c 100644
--- a/net/netfilter/ipvs/ip_vs_nfct.c
+++ b/net/netfilter/ipvs/ip_vs_nfct.c
@@ -208,7 +208,7 @@ alter:
* Use port 0 to expect connection from any port.
*/
void ip_vs_nfct_expect_related(struct sk_buff *skb, struct nf_conn *ct,
- struct ip_vs_conn *cp, u_int8_t proto,
+ struct ip_vs_conn *cp, u8 proto,
const __be16 port, int from_rs)
{
struct nf_conntrack_expect *exp;
diff --git a/net/netfilter/nf_conntrack_amanda.c b/net/netfilter/nf_conntrack_amanda.c
index ddafbdfc96dc..06d6ec12c86d 100644
--- a/net/netfilter/nf_conntrack_amanda.c
+++ b/net/netfilter/nf_conntrack_amanda.c
@@ -89,7 +89,7 @@ static int amanda_help(struct sk_buff *skb,
struct nf_conntrack_tuple *tuple;
unsigned int dataoff, start, stop, off, i;
char pbuf[sizeof("65535")], *tmp;
- u_int16_t len;
+ u16 len;
__be16 port;
int ret = NF_ACCEPT;
nf_nat_amanda_hook_fn *nf_nat_amanda;
@@ -199,10 +199,10 @@ static int __init nf_conntrack_amanda_init(void)
}
nf_ct_helper_init(&amanda_helper[0], AF_INET, IPPROTO_UDP,
- HELPER_NAME, 10080, 10080, 10080,
+ HELPER_NAME,
&amanda_exp_policy, 0, amanda_help, NULL, THIS_MODULE);
nf_ct_helper_init(&amanda_helper[1], AF_INET6, IPPROTO_UDP,
- HELPER_NAME, 10080, 10080, 10080,
+ HELPER_NAME,
&amanda_exp_policy, 0, amanda_help, NULL, THIS_MODULE);
ret = nf_conntrack_helpers_register(amanda_helper,
diff --git a/net/netfilter/nf_conntrack_broadcast.c b/net/netfilter/nf_conntrack_broadcast.c
index bf78828c7549..6ff954f1bfb8 100644
--- a/net/netfilter/nf_conntrack_broadcast.c
+++ b/net/netfilter/nf_conntrack_broadcast.c
@@ -66,8 +66,6 @@ int nf_conntrack_broadcast_help(struct sk_buff *skb,
exp->tuple = ct->tuplehash[IP_CT_DIR_REPLY].tuple;
helper = rcu_dereference(help->helper);
- if (helper)
- exp->tuple.src.u.udp.port = helper->tuple.src.u.udp.port;
exp->mask.src.u3.ip = mask;
exp->mask.src.u.udp.port = htons(0xFFFF);
diff --git a/net/netfilter/nf_conntrack_ftp.c b/net/netfilter/nf_conntrack_ftp.c
index 0847f845613d..f3944598c172 100644
--- a/net/netfilter/nf_conntrack_ftp.c
+++ b/net/netfilter/nf_conntrack_ftp.c
@@ -35,11 +35,6 @@ MODULE_ALIAS("ip_conntrack_ftp");
MODULE_ALIAS_NFCT_HELPER(HELPER_NAME);
static DEFINE_SPINLOCK(nf_ftp_lock);
-#define MAX_PORTS 8
-static u_int16_t ports[MAX_PORTS];
-static unsigned int ports_c;
-module_param_array(ports, ushort, &ports_c, 0400);
-
static bool loose;
module_param(loose, bool, 0600);
@@ -560,8 +555,8 @@ static int nf_ct_ftp_from_nlattr(struct nlattr *attr, struct nf_conn *ct)
return 0;
}
-static struct nf_conntrack_helper ftp[MAX_PORTS * 2] __read_mostly;
-static struct nf_conntrack_helper *ftp_ptr[MAX_PORTS * 2] __read_mostly;
+static struct nf_conntrack_helper ftp __read_mostly;
+static struct nf_conntrack_helper *ftp_ptr __read_mostly;
static const struct nf_conntrack_expect_policy ftp_exp_policy = {
.max_expected = 1,
@@ -570,32 +565,23 @@ static const struct nf_conntrack_expect_policy ftp_exp_policy = {
static void __exit nf_conntrack_ftp_fini(void)
{
- nf_conntrack_helpers_unregister(ftp_ptr, ports_c * 2);
+ nf_conntrack_helper_unregister(ftp_ptr);
}
static int __init nf_conntrack_ftp_init(void)
{
- int i, ret = 0;
+ int ret = 0;
NF_CT_HELPER_BUILD_BUG_ON(sizeof(struct nf_ct_ftp_master));
- if (ports_c == 0)
- ports[ports_c++] = FTP_PORT;
-
/* FIXME should be configurable whether IPv4 and IPv6 FTP connections
are tracked or not - YK */
- for (i = 0; i < ports_c; i++) {
- nf_ct_helper_init(&ftp[2 * i], AF_INET, IPPROTO_TCP,
- HELPER_NAME, FTP_PORT, ports[i], ports[i],
- &ftp_exp_policy, 0, help,
- nf_ct_ftp_from_nlattr, THIS_MODULE);
- nf_ct_helper_init(&ftp[2 * i + 1], AF_INET6, IPPROTO_TCP,
- HELPER_NAME, FTP_PORT, ports[i], ports[i],
- &ftp_exp_policy, 0, help,
- nf_ct_ftp_from_nlattr, THIS_MODULE);
- }
+ nf_ct_helper_init(&ftp, NFPROTO_UNSPEC, IPPROTO_TCP,
+ HELPER_NAME,
+ &ftp_exp_policy, 0, help,
+ nf_ct_ftp_from_nlattr, THIS_MODULE);
- ret = nf_conntrack_helpers_register(ftp, ports_c * 2, ftp_ptr);
+ ret = nf_conntrack_helper_register(&ftp, &ftp_ptr);
if (ret < 0) {
pr_err("failed to register helpers\n");
return ret;
diff --git a/net/netfilter/nf_conntrack_h323_main.c b/net/netfilter/nf_conntrack_h323_main.c
index 24931e379985..4cb1665bba02 100644
--- a/net/netfilter/nf_conntrack_h323_main.c
+++ b/net/netfilter/nf_conntrack_h323_main.c
@@ -671,7 +671,7 @@ static int expect_h245(struct sk_buff *skb, struct nf_conn *ct,
static int callforward_do_filter(struct net *net,
const union nf_inet_addr *src,
const union nf_inet_addr *dst,
- u_int8_t family)
+ u8 family)
{
int ret = 0;
@@ -1713,19 +1713,19 @@ static int __init h323_helper_init(void)
int ret;
nf_ct_helper_init(&nf_conntrack_helper_ras[0], AF_INET, IPPROTO_UDP,
- "RAS", RAS_PORT, RAS_PORT, RAS_PORT,
+ "RAS",
&ras_exp_policy, 0, ras_help, NULL, THIS_MODULE);
nf_ct_helper_init(&nf_conntrack_helper_ras[1], AF_INET6, IPPROTO_UDP,
- "RAS", RAS_PORT, RAS_PORT, RAS_PORT,
+ "RAS",
&ras_exp_policy, 0, ras_help, NULL, THIS_MODULE);
nf_ct_helper_init(&nf_conntrack_helper_h245, AF_UNSPEC, IPPROTO_UDP,
- "H.245", 0, 0, 0,
+ "H.245",
&h245_exp_policy, 0, h245_help, NULL, THIS_MODULE);
nf_ct_helper_init(&nf_conntrack_helper_q931[0], AF_INET, IPPROTO_TCP,
- "Q.931", Q931_PORT, Q931_PORT, Q931_PORT,
+ "Q.931",
&q931_exp_policy, 0, q931_help, NULL, THIS_MODULE);
nf_ct_helper_init(&nf_conntrack_helper_q931[1], AF_INET6, IPPROTO_TCP,
- "Q.931", Q931_PORT, Q931_PORT, Q931_PORT,
+ "Q.931",
&q931_exp_policy, 0, q931_help, NULL, THIS_MODULE);
ret = nf_conntrack_helper_register(&nf_conntrack_helper_h245,
diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c
index 500509b17663..506c58034761 100644
--- a/net/netfilter/nf_conntrack_helper.c
+++ b/net/netfilter/nf_conntrack_helper.c
@@ -40,12 +40,16 @@ static unsigned int nf_ct_helper_count __read_mostly;
static DEFINE_MUTEX(nf_ct_nat_helpers_mutex);
static struct list_head nf_ct_nat_helpers __read_mostly;
-/* Stupid hash, but collision free for the default registrations of the
- * helpers currently in the kernel. */
-static unsigned int helper_hash(const struct nf_conntrack_tuple *tuple)
+static unsigned int helper_hash(const char *name, u8 protonum)
{
- return (((tuple->src.l3num << 8) | tuple->dst.protonum) ^
- (__force __u16)tuple->src.u.all) % nf_ct_helper_hsize;
+ static u32 seed;
+ u32 initval;
+
+ get_random_once(&seed, sizeof(seed));
+
+ initval = seed ^ protonum;
+
+ return jhash(name, strlen(name), initval) % nf_ct_helper_hsize;
}
struct nf_conntrack_helper *
@@ -54,18 +58,18 @@ __nf_conntrack_helper_find(const char *name, u16 l3num, u8 protonum)
struct nf_conntrack_helper *h;
unsigned int i;
- for (i = 0; i < nf_ct_helper_hsize; i++) {
- hlist_for_each_entry_rcu(h, &nf_ct_helper_hash[i], hnode) {
- if (strcmp(h->name, name))
- continue;
+ if (!nf_ct_helper_hash)
+ return NULL;
- if (h->tuple.src.l3num != NFPROTO_UNSPEC &&
- h->tuple.src.l3num != l3num)
- continue;
+ i = helper_hash(name, protonum);
- if (h->tuple.dst.protonum == protonum)
- return h;
- }
+ hlist_for_each_entry_rcu(h, &nf_ct_helper_hash[i], hnode) {
+ if (strcmp(h->name, name))
+ continue;
+ if (h->nfproto != NFPROTO_UNSPEC && h->nfproto != l3num)
+ continue;
+ if (h->l4proto == protonum)
+ return h;
}
return NULL;
}
@@ -363,9 +367,8 @@ EXPORT_SYMBOL_GPL(nf_ct_helper_log);
int __nf_conntrack_helper_register(struct nf_conntrack_helper *me)
{
- struct nf_conntrack_tuple_mask mask = { .src.u.all = htons(0xFFFF) };
- unsigned int h = helper_hash(&me->tuple);
struct nf_conntrack_helper *cur;
+ unsigned int h;
int ret = 0, i;
BUG_ON(me->expect_class_max >= NF_CT_MAX_EXPECT_CLASSES);
@@ -382,29 +385,18 @@ int __nf_conntrack_helper_register(struct nf_conntrack_helper *me)
return -EINVAL;
}
+ h = helper_hash(me->name, me->l4proto);
mutex_lock(&nf_ct_helper_mutex);
- for (i = 0; i < nf_ct_helper_hsize; i++) {
- hlist_for_each_entry(cur, &nf_ct_helper_hash[i], hnode) {
- if (!strcmp(cur->name, me->name) &&
- (cur->tuple.src.l3num == NFPROTO_UNSPEC ||
- cur->tuple.src.l3num == me->tuple.src.l3num) &&
- cur->tuple.dst.protonum == me->tuple.dst.protonum) {
- ret = -EBUSY;
- goto out;
- }
+ hlist_for_each_entry(cur, &nf_ct_helper_hash[h], hnode) {
+ if (!strcmp(cur->name, me->name) &&
+ (cur->nfproto == NFPROTO_UNSPEC ||
+ cur->nfproto == me->nfproto) &&
+ cur->l4proto == me->l4proto) {
+ ret = -EBUSY;
+ goto out;
}
}
- /* avoid unpredictable behaviour for auto_assign_helper */
- if (!(me->flags & NF_CT_HELPER_F_USERSPACE)) {
- hlist_for_each_entry(cur, &nf_ct_helper_hash[h], hnode) {
- if (nf_ct_tuple_src_mask_cmp(&cur->tuple, &me->tuple,
- &mask)) {
- ret = -EBUSY;
- goto out;
- }
- }
- }
refcount_set(&me->ct_refcnt, 1);
hlist_add_head_rcu(&me->hnode, &nf_ct_helper_hash[h]);
nf_ct_helper_count++;
@@ -479,8 +471,7 @@ void nf_conntrack_helper_unregister(struct nf_conntrack_helper *me)
EXPORT_SYMBOL_GPL(nf_conntrack_helper_unregister);
void nf_ct_helper_init(struct nf_conntrack_helper *helper,
- u16 l3num, u16 protonum, const char *name,
- u16 default_port, u16 spec_port, u32 id,
+ u8 l3num, u16 protonum, const char *name,
const struct nf_conntrack_expect_policy *exp_pol,
u32 expect_class_max,
int (*help)(struct sk_buff *skb, unsigned int protoff,
@@ -492,9 +483,8 @@ void nf_ct_helper_init(struct nf_conntrack_helper *helper,
{
memset(helper, 0, sizeof(*helper));
- helper->tuple.src.l3num = l3num;
- helper->tuple.dst.protonum = protonum;
- helper->tuple.src.u.all = htons(spec_port);
+ helper->nfproto = l3num;
+ helper->l4proto = protonum;
rcu_assign_pointer(helper->help, help);
helper->from_nlattr = from_nlattr;
@@ -502,10 +492,7 @@ void nf_ct_helper_init(struct nf_conntrack_helper *helper,
snprintf(helper->nat_mod_name, sizeof(helper->nat_mod_name),
NF_NAT_HELPER_PREFIX "%s", name);
- if (spec_port == default_port)
- snprintf(helper->name, sizeof(helper->name), "%s", name);
- else
- snprintf(helper->name, sizeof(helper->name), "%s-%u", name, id);
+ snprintf(helper->name, sizeof(helper->name), "%s", name);
if (WARN_ON_ONCE(expect_class_max >= NF_CT_MAX_EXPECT_CLASSES))
return;
diff --git a/net/netfilter/nf_conntrack_irc.c b/net/netfilter/nf_conntrack_irc.c
index 193ab34db795..4e6bafe41437 100644
--- a/net/netfilter/nf_conntrack_irc.c
+++ b/net/netfilter/nf_conntrack_irc.c
@@ -21,9 +21,6 @@
#include <net/netfilter/nf_conntrack_helper.h>
#include <linux/netfilter/nf_conntrack_irc.h>
-#define MAX_PORTS 8
-static unsigned short ports[MAX_PORTS];
-static unsigned int ports_c;
static unsigned int max_dcc_channels = 8;
static unsigned int dcc_timeout __read_mostly = 300;
/* This is slow, but it's simple. --RR */
@@ -42,8 +39,6 @@ MODULE_LICENSE("GPL");
MODULE_ALIAS("ip_conntrack_irc");
MODULE_ALIAS_NFCT_HELPER(HELPER_NAME);
-module_param_array(ports, ushort, &ports_c, 0400);
-MODULE_PARM_DESC(ports, "port numbers of IRC servers");
module_param(max_dcc_channels, uint, 0400);
MODULE_PARM_DESC(max_dcc_channels, "max number of expected DCC channels per "
"IRC session");
@@ -254,13 +249,13 @@ static int help(struct sk_buff *skb, unsigned int protoff,
return ret;
}
-static struct nf_conntrack_helper irc[MAX_PORTS] __read_mostly;
-static struct nf_conntrack_helper *irc_ptr[MAX_PORTS] __read_mostly;
+static struct nf_conntrack_helper irc __read_mostly;
+static struct nf_conntrack_helper *irc_ptr __read_mostly;
static struct nf_conntrack_expect_policy irc_exp_policy;
static int __init nf_conntrack_irc_init(void)
{
- int i, ret;
+ int ret;
nf_conntrack_helper_deprecated(HELPER_NAME);
@@ -282,17 +277,11 @@ static int __init nf_conntrack_irc_init(void)
if (!irc_buffer)
return -ENOMEM;
- /* If no port given, default to standard irc port */
- if (ports_c == 0)
- ports[ports_c++] = IRC_PORT;
+ nf_ct_helper_init(&irc, AF_INET, IPPROTO_TCP, HELPER_NAME,
+ &irc_exp_policy,
+ 0, help, NULL, THIS_MODULE);
- for (i = 0; i < ports_c; i++) {
- nf_ct_helper_init(&irc[i], AF_INET, IPPROTO_TCP, HELPER_NAME,
- IRC_PORT, ports[i], i, &irc_exp_policy,
- 0, help, NULL, THIS_MODULE);
- }
-
- ret = nf_conntrack_helpers_register(&irc[0], ports_c, irc_ptr);
+ ret = nf_conntrack_helper_register(&irc, &irc_ptr);
if (ret) {
pr_err("failed to register helpers\n");
kfree(irc_buffer);
@@ -304,7 +293,7 @@ static int __init nf_conntrack_irc_init(void)
static void __exit nf_conntrack_irc_fini(void)
{
- nf_conntrack_helpers_unregister(irc_ptr, ports_c);
+ nf_conntrack_helper_unregister(irc_ptr);
kfree(irc_buffer);
}
diff --git a/net/netfilter/nf_conntrack_netbios_ns.c b/net/netfilter/nf_conntrack_netbios_ns.c
index 89d1cf7d6512..caa2b101fa9e 100644
--- a/net/netfilter/nf_conntrack_netbios_ns.c
+++ b/net/netfilter/nf_conntrack_netbios_ns.c
@@ -21,7 +21,6 @@
#include <net/netfilter/nf_conntrack_expect.h>
#define HELPER_NAME "netbios-ns"
-#define NMBD_PORT 137
MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
MODULE_DESCRIPTION("NetBIOS name service broadcast connection tracking helper");
@@ -54,7 +53,6 @@ static int __init nf_conntrack_netbios_ns_init(void)
exp_policy.timeout = timeout;
nf_ct_helper_init(&helper, AF_INET, IPPROTO_UDP, HELPER_NAME,
- NMBD_PORT, NMBD_PORT, NMBD_PORT,
&exp_policy, 0, netbios_ns_help, NULL, THIS_MODULE);
return nf_conntrack_helper_register(&helper, &helper_ptr);
diff --git a/net/netfilter/nf_conntrack_ovs.c b/net/netfilter/nf_conntrack_ovs.c
index 49d1511e9921..b4085af3ad1c 100644
--- a/net/netfilter/nf_conntrack_ovs.c
+++ b/net/netfilter/nf_conntrack_ovs.c
@@ -31,8 +31,8 @@ int nf_ct_helper(struct sk_buff *skb, struct nf_conn *ct,
if (!helper)
return NF_ACCEPT;
- if (helper->tuple.src.l3num != NFPROTO_UNSPEC &&
- helper->tuple.src.l3num != proto)
+ if (helper->nfproto != NFPROTO_UNSPEC &&
+ helper->nfproto != proto)
return NF_ACCEPT;
switch (proto) {
@@ -60,7 +60,7 @@ int nf_ct_helper(struct sk_buff *skb, struct nf_conn *ct,
return NF_DROP;
}
- if (helper->tuple.dst.protonum != proto)
+ if (helper->l4proto != proto)
return NF_ACCEPT;
helper_cb = rcu_dereference(helper->help);
diff --git a/net/netfilter/nf_conntrack_pptp.c b/net/netfilter/nf_conntrack_pptp.c
index 80fc14c87ddc..cbf32a3cb1f6 100644
--- a/net/netfilter/nf_conntrack_pptp.c
+++ b/net/netfilter/nf_conntrack_pptp.c
@@ -540,7 +540,7 @@ static int __init nf_conntrack_pptp_init(void)
NF_CT_HELPER_BUILD_BUG_ON(sizeof(struct nf_ct_pptp_master));
nf_ct_helper_init(&pptp, AF_INET, IPPROTO_TCP,
- "pptp", PPTP_CONTROL_PORT, PPTP_CONTROL_PORT, PPTP_CONTROL_PORT,
+ "pptp",
&pptp_exp_policy, 0, conntrack_pptp_help, NULL, THIS_MODULE);
pptp.destroy = gre_pptp_destroy_siblings;
diff --git a/net/netfilter/nf_conntrack_sane.c b/net/netfilter/nf_conntrack_sane.c
index 39085acf7a71..a0658f69d78f 100644
--- a/net/netfilter/nf_conntrack_sane.c
+++ b/net/netfilter/nf_conntrack_sane.c
@@ -34,11 +34,6 @@ MODULE_AUTHOR("Michal Schmidt <mschmidt@redhat.com>");
MODULE_DESCRIPTION("SANE connection tracking helper");
MODULE_ALIAS_NFCT_HELPER(HELPER_NAME);
-#define MAX_PORTS 8
-static u_int16_t ports[MAX_PORTS];
-static unsigned int ports_c;
-module_param_array(ports, ushort, &ports_c, 0400);
-
struct sane_request {
__be32 RPC_code;
#define SANE_NET_START 7 /* RPC code */
@@ -169,8 +164,8 @@ static int help(struct sk_buff *skb,
return ret;
}
-static struct nf_conntrack_helper sane[MAX_PORTS * 2] __read_mostly;
-static struct nf_conntrack_helper *sane_ptr[MAX_PORTS * 2] __read_mostly;
+static struct nf_conntrack_helper sane __read_mostly;
+static struct nf_conntrack_helper *sane_ptr __read_mostly;
static const struct nf_conntrack_expect_policy sane_exp_policy = {
.max_expected = 1,
@@ -179,32 +174,21 @@ static const struct nf_conntrack_expect_policy sane_exp_policy = {
static void __exit nf_conntrack_sane_fini(void)
{
- nf_conntrack_helpers_unregister(sane_ptr, ports_c * 2);
+ nf_conntrack_helper_unregister(sane_ptr);
}
static int __init nf_conntrack_sane_init(void)
{
- int i, ret = 0;
+ int ret = 0;
NF_CT_HELPER_BUILD_BUG_ON(sizeof(struct nf_ct_sane_master));
- if (ports_c == 0)
- ports[ports_c++] = SANE_PORT;
-
- /* FIXME should be configurable whether IPv4 and IPv6 connections
- are tracked or not - YK */
- for (i = 0; i < ports_c; i++) {
- nf_ct_helper_init(&sane[2 * i], AF_INET, IPPROTO_TCP,
- HELPER_NAME, SANE_PORT, ports[i], ports[i],
- &sane_exp_policy, 0, help, NULL,
- THIS_MODULE);
- nf_ct_helper_init(&sane[2 * i + 1], AF_INET6, IPPROTO_TCP,
- HELPER_NAME, SANE_PORT, ports[i], ports[i],
- &sane_exp_policy, 0, help, NULL,
- THIS_MODULE);
- }
+ nf_ct_helper_init(&sane, NFPROTO_UNSPEC, IPPROTO_TCP,
+ HELPER_NAME,
+ &sane_exp_policy, 0, help, NULL,
+ THIS_MODULE);
- ret = nf_conntrack_helpers_register(sane, ports_c * 2, sane_ptr);
+ ret = nf_conntrack_helper_register(&sane, &sane_ptr);
if (ret < 0) {
pr_err("failed to register helpers\n");
return ret;
diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c
index f3f90a866338..0ff089e03891 100644
--- a/net/netfilter/nf_conntrack_sip.c
+++ b/net/netfilter/nf_conntrack_sip.c
@@ -35,12 +35,6 @@ MODULE_DESCRIPTION("SIP connection tracking helper");
MODULE_ALIAS("ip_conntrack_sip");
MODULE_ALIAS_NFCT_HELPER(HELPER_NAME);
-#define MAX_PORTS 8
-static unsigned short ports[MAX_PORTS];
-static unsigned int ports_c;
-module_param_array(ports, ushort, &ports_c, 0400);
-MODULE_PARM_DESC(ports, "port numbers of SIP servers");
-
static unsigned int sip_timeout __read_mostly = SIP_TIMEOUT;
module_param(sip_timeout, uint, 0600);
MODULE_PARM_DESC(sip_timeout, "timeout for the master SIP session");
@@ -1767,8 +1761,8 @@ static int sip_help_udp(struct sk_buff *skb, unsigned int protoff,
return process_sip_msg(skb, ct, protoff, dataoff, &dptr, &datalen);
}
-static struct nf_conntrack_helper sip[MAX_PORTS * 4] __read_mostly;
-static struct nf_conntrack_helper *sip_ptr[MAX_PORTS * 4] __read_mostly;
+static struct nf_conntrack_helper sip[2] __read_mostly;
+static struct nf_conntrack_helper *sip_ptr[2] __read_mostly;
static const struct nf_conntrack_expect_policy sip_exp_policy[SIP_EXPECT_MAX + 1] = {
[SIP_EXPECT_SIGNALLING] = {
@@ -1795,38 +1789,25 @@ static const struct nf_conntrack_expect_policy sip_exp_policy[SIP_EXPECT_MAX + 1
static void __exit nf_conntrack_sip_fini(void)
{
- nf_conntrack_helpers_unregister(sip_ptr, ports_c * 4);
+ nf_conntrack_helpers_unregister(sip_ptr, 2);
}
static int __init nf_conntrack_sip_init(void)
{
- int i, ret;
+ int ret;
NF_CT_HELPER_BUILD_BUG_ON(sizeof(struct nf_ct_sip_master));
- if (ports_c == 0)
- ports[ports_c++] = SIP_PORT;
-
- for (i = 0; i < ports_c; i++) {
- nf_ct_helper_init(&sip[4 * i], AF_INET, IPPROTO_UDP,
- HELPER_NAME, SIP_PORT, ports[i], i,
- sip_exp_policy, SIP_EXPECT_MAX, sip_help_udp,
- NULL, THIS_MODULE);
- nf_ct_helper_init(&sip[4 * i + 1], AF_INET, IPPROTO_TCP,
- HELPER_NAME, SIP_PORT, ports[i], i,
- sip_exp_policy, SIP_EXPECT_MAX, sip_help_tcp,
- NULL, THIS_MODULE);
- nf_ct_helper_init(&sip[4 * i + 2], AF_INET6, IPPROTO_UDP,
- HELPER_NAME, SIP_PORT, ports[i], i,
- sip_exp_policy, SIP_EXPECT_MAX, sip_help_udp,
- NULL, THIS_MODULE);
- nf_ct_helper_init(&sip[4 * i + 3], AF_INET6, IPPROTO_TCP,
- HELPER_NAME, SIP_PORT, ports[i], i,
- sip_exp_policy, SIP_EXPECT_MAX, sip_help_tcp,
- NULL, THIS_MODULE);
- }
+ nf_ct_helper_init(&sip[0], NFPROTO_UNSPEC, IPPROTO_UDP,
+ HELPER_NAME,
+ sip_exp_policy, SIP_EXPECT_MAX, sip_help_udp,
+ NULL, THIS_MODULE);
+ nf_ct_helper_init(&sip[1], NFPROTO_UNSPEC, IPPROTO_TCP,
+ HELPER_NAME,
+ sip_exp_policy, SIP_EXPECT_MAX, sip_help_tcp,
+ NULL, THIS_MODULE);
- ret = nf_conntrack_helpers_register(sip, ports_c * 4, sip_ptr);
+ ret = nf_conntrack_helpers_register(sip, 2, sip_ptr);
if (ret < 0) {
pr_err("failed to register helpers\n");
return ret;
diff --git a/net/netfilter/nf_conntrack_snmp.c b/net/netfilter/nf_conntrack_snmp.c
index b6fce5703fce..109986d5d55e 100644
--- a/net/netfilter/nf_conntrack_snmp.c
+++ b/net/netfilter/nf_conntrack_snmp.c
@@ -14,8 +14,6 @@
#include <net/netfilter/nf_conntrack_expect.h>
#include <linux/netfilter/nf_conntrack_snmp.h>
-#define SNMP_PORT 161
-
MODULE_AUTHOR("Jiri Olsa <jolsa@redhat.com>");
MODULE_DESCRIPTION("SNMP service broadcast connection tracking helper");
MODULE_LICENSE("GPL");
@@ -55,7 +53,7 @@ static int __init nf_conntrack_snmp_init(void)
exp_policy.timeout = timeout;
nf_ct_helper_init(&helper, AF_INET, IPPROTO_UDP,
- "snmp", SNMP_PORT, SNMP_PORT, SNMP_PORT,
+ "snmp",
&exp_policy, 0, snmp_conntrack_help, NULL,
THIS_MODULE);
diff --git a/net/netfilter/nf_conntrack_tftp.c b/net/netfilter/nf_conntrack_tftp.c
index 4393c435aa35..a69559edf9b3 100644
--- a/net/netfilter/nf_conntrack_tftp.c
+++ b/net/netfilter/nf_conntrack_tftp.c
@@ -26,12 +26,6 @@ MODULE_LICENSE("GPL");
MODULE_ALIAS("ip_conntrack_tftp");
MODULE_ALIAS_NFCT_HELPER(HELPER_NAME);
-#define MAX_PORTS 8
-static unsigned short ports[MAX_PORTS];
-static unsigned int ports_c;
-module_param_array(ports, ushort, &ports_c, 0400);
-MODULE_PARM_DESC(ports, "Port numbers of TFTP servers");
-
nf_nat_tftp_hook_fn __rcu *nf_nat_tftp_hook __read_mostly;
EXPORT_SYMBOL_GPL(nf_nat_tftp_hook);
@@ -95,8 +89,8 @@ static int tftp_help(struct sk_buff *skb,
return ret;
}
-static struct nf_conntrack_helper tftp[MAX_PORTS * 2] __read_mostly;
-static struct nf_conntrack_helper *tftp_ptr[MAX_PORTS * 2] __read_mostly;
+static struct nf_conntrack_helper tftp __read_mostly;
+static struct nf_conntrack_helper *tftp_ptr __read_mostly;
static const struct nf_conntrack_expect_policy tftp_exp_policy = {
.max_expected = 1,
@@ -105,30 +99,21 @@ static const struct nf_conntrack_expect_policy tftp_exp_policy = {
static void __exit nf_conntrack_tftp_fini(void)
{
- nf_conntrack_helpers_unregister(tftp_ptr, ports_c * 2);
+ nf_conntrack_helper_unregister(tftp_ptr);
}
static int __init nf_conntrack_tftp_init(void)
{
- int i, ret;
+ int ret;
NF_CT_HELPER_BUILD_BUG_ON(0);
- if (ports_c == 0)
- ports[ports_c++] = TFTP_PORT;
-
- for (i = 0; i < ports_c; i++) {
- nf_ct_helper_init(&tftp[2 * i], AF_INET, IPPROTO_UDP,
- HELPER_NAME, TFTP_PORT, ports[i], i,
- &tftp_exp_policy, 0, tftp_help, NULL,
- THIS_MODULE);
- nf_ct_helper_init(&tftp[2 * i + 1], AF_INET6, IPPROTO_UDP,
- HELPER_NAME, TFTP_PORT, ports[i], i,
- &tftp_exp_policy, 0, tftp_help, NULL,
- THIS_MODULE);
- }
+ nf_ct_helper_init(&tftp, NFPROTO_UNSPEC, IPPROTO_UDP,
+ HELPER_NAME,
+ &tftp_exp_policy, 0, tftp_help, NULL,
+ THIS_MODULE);
- ret = nf_conntrack_helpers_register(tftp, ports_c * 2, tftp_ptr);
+ ret = nf_conntrack_helper_register(&tftp, &tftp_ptr);
if (ret < 0) {
pr_err("failed to register helpers\n");
return ret;
diff --git a/net/netfilter/nf_nat_core.c b/net/netfilter/nf_nat_core.c
index 63ff6b4d5d21..8ac326e1eb5b 100644
--- a/net/netfilter/nf_nat_core.c
+++ b/net/netfilter/nf_nat_core.c
@@ -39,12 +39,6 @@ static struct hlist_head *nf_nat_bysource __read_mostly;
static unsigned int nf_nat_htable_size __read_mostly;
static siphash_aligned_key_t nf_nat_hash_rnd;
-struct nf_nat_lookup_hook_priv {
- struct nf_hook_entries __rcu *entries;
-
- struct rcu_head rcu_head;
-};
-
struct nf_nat_hooks_net {
struct nf_hook_ops *nat_hook_ops;
unsigned int users;
diff --git a/net/netfilter/nf_nat_proto.c b/net/netfilter/nf_nat_proto.c
index 07f51fe75fbe..64b9bac228ea 100644
--- a/net/netfilter/nf_nat_proto.c
+++ b/net/netfilter/nf_nat_proto.c
@@ -770,6 +770,7 @@ static const struct nf_hook_ops nf_nat_ipv4_ops[] = {
.pf = NFPROTO_IPV4,
.hooknum = NF_INET_PRE_ROUTING,
.priority = NF_IP_PRI_NAT_DST,
+ .hook_ops_type = NF_HOOK_OP_NAT,
},
/* After packet filtering, change source */
{
@@ -777,6 +778,7 @@ static const struct nf_hook_ops nf_nat_ipv4_ops[] = {
.pf = NFPROTO_IPV4,
.hooknum = NF_INET_POST_ROUTING,
.priority = NF_IP_PRI_NAT_SRC,
+ .hook_ops_type = NF_HOOK_OP_NAT,
},
/* Before packet filtering, change destination */
{
@@ -784,6 +786,7 @@ static const struct nf_hook_ops nf_nat_ipv4_ops[] = {
.pf = NFPROTO_IPV4,
.hooknum = NF_INET_LOCAL_OUT,
.priority = NF_IP_PRI_NAT_DST,
+ .hook_ops_type = NF_HOOK_OP_NAT,
},
/* After packet filtering, change source */
{
@@ -791,6 +794,7 @@ static const struct nf_hook_ops nf_nat_ipv4_ops[] = {
.pf = NFPROTO_IPV4,
.hooknum = NF_INET_LOCAL_IN,
.priority = NF_IP_PRI_NAT_SRC,
+ .hook_ops_type = NF_HOOK_OP_NAT,
},
};
@@ -1031,6 +1035,7 @@ static const struct nf_hook_ops nf_nat_ipv6_ops[] = {
.pf = NFPROTO_IPV6,
.hooknum = NF_INET_PRE_ROUTING,
.priority = NF_IP6_PRI_NAT_DST,
+ .hook_ops_type = NF_HOOK_OP_NAT,
},
/* After packet filtering, change source */
{
@@ -1038,6 +1043,7 @@ static const struct nf_hook_ops nf_nat_ipv6_ops[] = {
.pf = NFPROTO_IPV6,
.hooknum = NF_INET_POST_ROUTING,
.priority = NF_IP6_PRI_NAT_SRC,
+ .hook_ops_type = NF_HOOK_OP_NAT,
},
/* Before packet filtering, change destination */
{
@@ -1045,6 +1051,7 @@ static const struct nf_hook_ops nf_nat_ipv6_ops[] = {
.pf = NFPROTO_IPV6,
.hooknum = NF_INET_LOCAL_OUT,
.priority = NF_IP6_PRI_NAT_DST,
+ .hook_ops_type = NF_HOOK_OP_NAT,
},
/* After packet filtering, change source */
{
@@ -1052,6 +1059,7 @@ static const struct nf_hook_ops nf_nat_ipv6_ops[] = {
.pf = NFPROTO_IPV6,
.hooknum = NF_INET_LOCAL_IN,
.priority = NF_IP6_PRI_NAT_SRC,
+ .hook_ops_type = NF_HOOK_OP_NAT,
},
};
diff --git a/net/netfilter/nfnetlink_cthelper.c b/net/netfilter/nfnetlink_cthelper.c
index f062ac210343..5b6a09f46903 100644
--- a/net/netfilter/nfnetlink_cthelper.c
+++ b/net/netfilter/nfnetlink_cthelper.c
@@ -67,7 +67,7 @@ nfnl_userspace_cthelper(struct sk_buff *skb, unsigned int protoff,
}
static const struct nla_policy nfnl_cthelper_tuple_pol[NFCTH_TUPLE_MAX+1] = {
- [NFCTH_TUPLE_L3PROTONUM] = { .type = NLA_U16, },
+ [NFCTH_TUPLE_L3PROTONUM] = NLA_POLICY_MAX(NLA_BE16, NFPROTO_IPV6),
[NFCTH_TUPLE_L4PROTONUM] = { .type = NLA_U8, },
};
@@ -256,7 +256,8 @@ nfnl_cthelper_create(const struct nlattr * const tb[],
helper->data_len = size;
helper->flags |= NF_CT_HELPER_F_USERSPACE;
- memcpy(&helper->tuple, tuple, sizeof(struct nf_conntrack_tuple));
+ helper->nfproto = tuple->src.l3num;
+ helper->l4proto = tuple->dst.protonum;
helper->me = THIS_MODULE;
helper->help = nfnl_userspace_cthelper;
@@ -453,8 +454,8 @@ static int nfnl_cthelper_new(struct sk_buff *skb, const struct nfnl_info *info,
if (strncmp(cur->name, helper_name, NF_CT_HELPER_NAME_LEN))
continue;
- if ((tuple.src.l3num != cur->tuple.src.l3num ||
- tuple.dst.protonum != cur->tuple.dst.protonum))
+ if ((tuple.src.l3num != cur->nfproto ||
+ tuple.dst.protonum != cur->l4proto))
continue;
if (info->nlh->nlmsg_flags & NLM_F_EXCL)
@@ -483,10 +484,10 @@ nfnl_cthelper_dump_tuple(struct sk_buff *skb,
goto nla_put_failure;
if (nla_put_be16(skb, NFCTH_TUPLE_L3PROTONUM,
- htons(helper->tuple.src.l3num)))
+ htons(helper->nfproto)))
goto nla_put_failure;
- if (nla_put_u8(skb, NFCTH_TUPLE_L4PROTONUM, helper->tuple.dst.protonum))
+ if (nla_put_u8(skb, NFCTH_TUPLE_L4PROTONUM, helper->l4proto))
goto nla_put_failure;
nla_nest_end(skb, nest_parms);
@@ -665,8 +666,8 @@ static int nfnl_cthelper_get(struct sk_buff *skb, const struct nfnl_info *info,
continue;
if (tuple_set &&
- (tuple.src.l3num != cur->tuple.src.l3num ||
- tuple.dst.protonum != cur->tuple.dst.protonum))
+ (tuple.src.l3num != cur->nfproto ||
+ tuple.dst.protonum != cur->l4proto))
continue;
skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
@@ -725,8 +726,8 @@ static int nfnl_cthelper_del(struct sk_buff *skb, const struct nfnl_info *info,
continue;
if (tuple_set &&
- (tuple.src.l3num != cur->tuple.src.l3num ||
- tuple.dst.protonum != cur->tuple.dst.protonum))
+ (tuple.src.l3num != cur->nfproto ||
+ tuple.dst.protonum != cur->l4proto))
continue;
found = true;
diff --git a/net/netfilter/nfnetlink_cttimeout.c b/net/netfilter/nfnetlink_cttimeout.c
index 170d3db860c5..66c2016f6049 100644
--- a/net/netfilter/nfnetlink_cttimeout.c
+++ b/net/netfilter/nfnetlink_cttimeout.c
@@ -168,7 +168,7 @@ static int cttimeout_new_timeout(struct sk_buff *skb,
if (ret < 0)
goto err_free_timeout_policy;
- strcpy(timeout->name, nla_data(cda[CTA_TIMEOUT_NAME]));
+ nla_strscpy(timeout->name, cda[CTA_TIMEOUT_NAME], sizeof(timeout->name));
timeout->timeout->l3num = l3num;
timeout->timeout->l4proto = l4proto;
refcount_set(&timeout->timeout->refcnt, 1);
diff --git a/net/netfilter/nfnetlink_hook.c b/net/netfilter/nfnetlink_hook.c
index 5623c18fcd12..95005e9a6066 100644
--- a/net/netfilter/nfnetlink_hook.c
+++ b/net/netfilter/nfnetlink_hook.c
@@ -190,7 +190,7 @@ static int nfnl_hook_put_nft_ft_info(struct sk_buff *nlskb,
static int nfnl_hook_dump_one(struct sk_buff *nlskb,
const struct nfnl_dump_hook_data *ctx,
- const struct nf_hook_ops *ops,
+ const struct nf_hook_ops *ops, int priority,
int family, unsigned int seq)
{
u16 event = nfnl_msg_type(NFNL_SUBSYS_HOOK, NFNL_MSG_HOOK_GET);
@@ -244,7 +244,7 @@ static int nfnl_hook_dump_one(struct sk_buff *nlskb,
if (ret)
goto nla_put_failure;
- ret = nla_put_be32(nlskb, NFNLA_HOOK_PRIORITY, htonl(ops->priority));
+ ret = nla_put_be32(nlskb, NFNLA_HOOK_PRIORITY, htonl(priority));
if (ret)
goto nla_put_failure;
@@ -337,6 +337,30 @@ nfnl_hook_entries_head(u8 pf, unsigned int hook, struct net *net, const char *de
return hook_head;
}
+static int nfnl_hook_dump_nat(struct sk_buff *nlskb,
+ const struct nfnl_dump_hook_data *ctx,
+ const struct nf_hook_ops *ops,
+ int family, unsigned int seq)
+{
+ struct nf_nat_lookup_hook_priv *priv = ops->priv;
+ struct nf_hook_entries *e = rcu_dereference(priv->entries);
+ struct nf_hook_ops **nat_ops;
+ int i, err;
+
+ if (!e)
+ return 0;
+
+ nat_ops = nf_hook_entries_get_hook_ops(e);
+
+ for (i = 0; i < e->num_hook_entries; i++) {
+ err = nfnl_hook_dump_one(nlskb, ctx, nat_ops[i],
+ ops->priority, family, seq);
+ if (err)
+ return err;
+ }
+ return 0;
+}
+
static int nfnl_hook_dump(struct sk_buff *nlskb,
struct netlink_callback *cb)
{
@@ -365,8 +389,13 @@ static int nfnl_hook_dump(struct sk_buff *nlskb,
ops = nf_hook_entries_get_hook_ops(e);
for (; i < e->num_hook_entries; i++) {
- err = nfnl_hook_dump_one(nlskb, ctx, ops[i], family,
- cb->nlh->nlmsg_seq);
+ if (ops[i]->hook_ops_type == NF_HOOK_OP_NAT)
+ err = nfnl_hook_dump_nat(nlskb, ctx, ops[i], family,
+ cb->nlh->nlmsg_seq);
+ else
+ err = nfnl_hook_dump_one(nlskb, ctx, ops[i],
+ ops[i]->priority, family,
+ cb->nlh->nlmsg_seq);
if (err)
break;
}
diff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c
index 03a88c77e0f0..358b9287e12e 100644
--- a/net/netfilter/nft_ct.c
+++ b/net/netfilter/nft_ct.c
@@ -1297,6 +1297,17 @@ static int nft_ct_expect_obj_dump(struct sk_buff *skb,
return 0;
}
+#if IS_ENABLED(CONFIG_NF_NAT)
+static void nft_ct_nat_follow_master(struct nf_conn *ct, struct nf_conntrack_expect *this)
+{
+ const struct nf_ct_helper_expectfn *expfn;
+
+ expfn = nf_ct_helper_expectfn_find_by_name("nat-follow-master");
+ if (expfn)
+ expfn->expectfn(ct, this);
+}
+#endif
+
static void nft_ct_expect_obj_eval(struct nft_object *obj,
struct nft_regs *regs,
const struct nft_pktinfo *pkt)
@@ -1342,6 +1353,13 @@ static void nft_ct_expect_obj_eval(struct nft_object *obj,
priv->l4proto, NULL, &priv->dport);
exp->timeout += priv->timeout;
+#if IS_ENABLED(CONFIG_NF_NAT)
+ if (ct->status & IPS_NAT_MASK) {
+ exp->saved_proto.tcp.port = priv->dport;
+ exp->dir = !dir;
+ exp->expectfn = nft_ct_nat_follow_master;
+ }
+#endif
if (nf_ct_expect_related(exp, 0) != 0)
regs->verdict.code = NF_DROP;
@@ -1375,6 +1393,13 @@ static struct nft_object_type nft_ct_expect_obj_type __read_mostly = {
.owner = THIS_MODULE,
};
+#if IS_ENABLED(CONFIG_NF_NAT)
+static struct nf_ct_helper_expectfn nft_ct_nat __read_mostly = {
+ .name = "nft_ct-follow-master",
+ .expectfn = nft_ct_nat_follow_master,
+};
+#endif
+
static int __init nft_ct_module_init(void)
{
int err;
@@ -1401,6 +1426,9 @@ static int __init nft_ct_module_init(void)
if (err < 0)
goto err4;
#endif
+#if IS_ENABLED(CONFIG_NF_NAT)
+ nf_ct_helper_expectfn_register(&nft_ct_nat);
+#endif
return 0;
#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
@@ -1425,6 +1453,13 @@ static void __exit nft_ct_module_exit(void)
nft_unregister_obj(&nft_ct_helper_obj_type);
nft_unregister_expr(&nft_notrack_type);
nft_unregister_expr(&nft_ct_type);
+
+#if IS_ENABLED(CONFIG_NF_NAT)
+ nf_ct_helper_expectfn_unregister(&nft_ct_nat);
+ synchronize_rcu();
+ nf_ct_helper_expectfn_destroy(&nft_ct_nat);
+ synchronize_rcu();
+#endif
}
module_init(nft_ct_module_init);
diff --git a/net/netfilter/nft_set_rbtree.c b/net/netfilter/nft_set_rbtree.c
index 6222e9bb57bc..9894832281c4 100644
--- a/net/netfilter/nft_set_rbtree.c
+++ b/net/netfilter/nft_set_rbtree.c
@@ -548,8 +548,7 @@ static int nft_array_intervals_alloc(struct nft_array *array, u32 max_intervals)
if (!intervals)
return -ENOMEM;
- if (array->intervals)
- kvfree(array->intervals);
+ kvfree(array->intervals);
array->intervals = intervals;
array->max_intervals = max_intervals;
diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c
index 4e6708c23922..e64116bf2637 100644
--- a/net/netfilter/x_tables.c
+++ b/net/netfilter/x_tables.c
@@ -1920,9 +1920,9 @@ static const struct seq_operations xt_target_seq_ops = {
.show = xt_target_seq_show,
};
-#define FORMAT_TABLES "_tables_names"
-#define FORMAT_MATCHES "_tables_matches"
-#define FORMAT_TARGETS "_tables_targets"
+#define FORMAT_TABLES "%s_tables_names"
+#define FORMAT_MATCHES "%s_tables_matches"
+#define FORMAT_TARGETS "%s_tables_targets"
#endif /* CONFIG_PROC_FS */
@@ -2033,8 +2033,7 @@ int xt_proto_init(struct net *net, u_int8_t af)
root_uid = make_kuid(net->user_ns, 0);
root_gid = make_kgid(net->user_ns, 0);
- strscpy(buf, xt_prefix[af], sizeof(buf));
- strlcat(buf, FORMAT_TABLES, sizeof(buf));
+ snprintf(buf, sizeof(buf), FORMAT_TABLES, xt_prefix[af]);
proc = proc_create_net_data(buf, 0440, net->proc_net, &xt_table_seq_ops,
sizeof(struct seq_net_private),
(void *)(unsigned long)af);
@@ -2043,8 +2042,7 @@ int xt_proto_init(struct net *net, u_int8_t af)
if (uid_valid(root_uid) && gid_valid(root_gid))
proc_set_user(proc, root_uid, root_gid);
- strscpy(buf, xt_prefix[af], sizeof(buf));
- strlcat(buf, FORMAT_MATCHES, sizeof(buf));
+ snprintf(buf, sizeof(buf), FORMAT_MATCHES, xt_prefix[af]);
proc = proc_create_seq_private(buf, 0440, net->proc_net,
&xt_match_seq_ops, sizeof(struct nf_mttg_trav),
(void *)(unsigned long)af);
@@ -2053,8 +2051,7 @@ int xt_proto_init(struct net *net, u_int8_t af)
if (uid_valid(root_uid) && gid_valid(root_gid))
proc_set_user(proc, root_uid, root_gid);
- strscpy(buf, xt_prefix[af], sizeof(buf));
- strlcat(buf, FORMAT_TARGETS, sizeof(buf));
+ snprintf(buf, sizeof(buf), FORMAT_TARGETS, xt_prefix[af]);
proc = proc_create_seq_private(buf, 0440, net->proc_net,
&xt_target_seq_ops, sizeof(struct nf_mttg_trav),
(void *)(unsigned long)af);
@@ -2068,13 +2065,11 @@ int xt_proto_init(struct net *net, u_int8_t af)
#ifdef CONFIG_PROC_FS
out_remove_matches:
- strscpy(buf, xt_prefix[af], sizeof(buf));
- strlcat(buf, FORMAT_MATCHES, sizeof(buf));
+ snprintf(buf, sizeof(buf), FORMAT_MATCHES, xt_prefix[af]);
remove_proc_entry(buf, net->proc_net);
out_remove_tables:
- strscpy(buf, xt_prefix[af], sizeof(buf));
- strlcat(buf, FORMAT_TABLES, sizeof(buf));
+ snprintf(buf, sizeof(buf), FORMAT_TABLES, xt_prefix[af]);
remove_proc_entry(buf, net->proc_net);
out:
return -1;
@@ -2087,16 +2082,13 @@ void xt_proto_fini(struct net *net, u_int8_t af)
#ifdef CONFIG_PROC_FS
char buf[XT_FUNCTION_MAXNAMELEN];
- strscpy(buf, xt_prefix[af], sizeof(buf));
- strlcat(buf, FORMAT_TABLES, sizeof(buf));
+ snprintf(buf, sizeof(buf), FORMAT_TABLES, xt_prefix[af]);
remove_proc_entry(buf, net->proc_net);
- strscpy(buf, xt_prefix[af], sizeof(buf));
- strlcat(buf, FORMAT_TARGETS, sizeof(buf));
+ snprintf(buf, sizeof(buf), FORMAT_TARGETS, xt_prefix[af]);
remove_proc_entry(buf, net->proc_net);
- strscpy(buf, xt_prefix[af], sizeof(buf));
- strlcat(buf, FORMAT_MATCHES, sizeof(buf));
+ snprintf(buf, sizeof(buf), FORMAT_MATCHES, xt_prefix[af]);
remove_proc_entry(buf, net->proc_net);
#endif /*CONFIG_PROC_FS*/
}
diff --git a/net/netfilter/xt_TCPOPTSTRIP.c b/net/netfilter/xt_TCPOPTSTRIP.c
index 93f064306901..265d21697847 100644
--- a/net/netfilter/xt_TCPOPTSTRIP.c
+++ b/net/netfilter/xt_TCPOPTSTRIP.c
@@ -16,7 +16,7 @@
#include <linux/netfilter/x_tables.h>
#include <linux/netfilter/xt_TCPOPTSTRIP.h>
-static inline unsigned int optlen(const u_int8_t *opt, unsigned int offset)
+static inline unsigned int optlen(const u8 *opt, unsigned int offset)
{
/* Beware zero-length options: make finite progress */
if (opt[offset] <= TCPOPT_NOP || opt[offset+1] == 0)
@@ -33,8 +33,8 @@ tcpoptstrip_mangle_packet(struct sk_buff *skb,
const struct xt_tcpoptstrip_target_info *info = par->targinfo;
struct tcphdr *tcph, _th;
unsigned int optl, i, j;
- u_int16_t n, o;
- u_int8_t *opt;
+ u16 n, o;
+ u8 *opt;
int tcp_hdrlen;
/* This is a fragment, no TCP header is available */
@@ -97,7 +97,7 @@ tcpoptstrip_tg6(struct sk_buff *skb, const struct xt_action_param *par)
{
struct ipv6hdr *ipv6h = ipv6_hdr(skb);
int tcphoff;
- u_int8_t nexthdr;
+ u8 nexthdr;
__be16 frag_off;
nexthdr = ipv6h->nexthdr;
diff --git a/net/netfilter/xt_dscp.c b/net/netfilter/xt_dscp.c
index fb0169a8f9bb..878f27016e99 100644
--- a/net/netfilter/xt_dscp.c
+++ b/net/netfilter/xt_dscp.c
@@ -49,6 +49,16 @@ static int dscp_mt_check(const struct xt_mtchk_param *par)
return 0;
}
+static int tos_mt_check(const struct xt_mtchk_param *par)
+{
+ const struct xt_tos_match_info *info = par->matchinfo;
+
+ if (info->invert > 1)
+ return -EINVAL;
+
+ return 0;
+}
+
static bool tos_mt(const struct sk_buff *skb, struct xt_action_param *par)
{
const struct xt_tos_match_info *info = par->matchinfo;
@@ -82,6 +92,7 @@ static struct xt_match dscp_mt_reg[] __read_mostly = {
.name = "tos",
.revision = 1,
.family = NFPROTO_IPV4,
+ .checkentry = tos_mt_check,
.match = tos_mt,
.matchsize = sizeof(struct xt_tos_match_info),
.me = THIS_MODULE,
@@ -90,6 +101,7 @@ static struct xt_match dscp_mt_reg[] __read_mostly = {
.name = "tos",
.revision = 1,
.family = NFPROTO_IPV6,
+ .checkentry = tos_mt_check,
.match = tos_mt,
.matchsize = sizeof(struct xt_tos_match_info),
.me = THIS_MODULE,
diff --git a/net/netfilter/xt_recent.c b/net/netfilter/xt_recent.c
index f72752fa4374..d34831ce3adf 100644
--- a/net/netfilter/xt_recent.c
+++ b/net/netfilter/xt_recent.c
@@ -400,7 +400,7 @@ static int recent_mt_check(const struct xt_mtchk_param *par,
t->nstamps_max_mask = nstamp_mask;
memcpy(&t->mask, &info->mask, sizeof(t->mask));
- strcpy(t->name, info->name);
+ strscpy(t->name, info->name);
INIT_LIST_HEAD(&t->lru_list);
for (i = 0; i < ip_list_hash_size; i++)
INIT_LIST_HEAD(&t->iphash[i]);
diff --git a/net/netfilter/xt_tcpmss.c b/net/netfilter/xt_tcpmss.c
index b9da8269161d..b08b077d7f0a 100644
--- a/net/netfilter/xt_tcpmss.c
+++ b/net/netfilter/xt_tcpmss.c
@@ -78,10 +78,23 @@ dropit:
return false;
}
+static int tcpmss_mt_check(const struct xt_mtchk_param *par)
+{
+ const struct xt_tcpmss_match_info *info = par->matchinfo;
+
+ if (info->mss_min > info->mss_max)
+ return -EINVAL;
+ if (info->invert > 1)
+ return -EINVAL;
+
+ return 0;
+}
+
static struct xt_match tcpmss_mt_reg[] __read_mostly = {
{
.name = "tcpmss",
.family = NFPROTO_IPV4,
+ .checkentry = tcpmss_mt_check,
.match = tcpmss_mt,
.matchsize = sizeof(struct xt_tcpmss_match_info),
.proto = IPPROTO_TCP,
diff --git a/net/sched/act_ct.c b/net/sched/act_ct.c
index be535a261fa0..4ca7964e83c8 100644
--- a/net/sched/act_ct.c
+++ b/net/sched/act_ct.c
@@ -1527,8 +1527,8 @@ static int tcf_ct_dump_helper(struct sk_buff *skb,
return 0;
if (nla_put_string(skb, TCA_CT_HELPER_NAME, helper->name) ||
- nla_put_u8(skb, TCA_CT_HELPER_FAMILY, helper->tuple.src.l3num) ||
- nla_put_u8(skb, TCA_CT_HELPER_PROTO, helper->tuple.dst.protonum))
+ nla_put_u8(skb, TCA_CT_HELPER_FAMILY, helper->nfproto) ||
+ nla_put_u8(skb, TCA_CT_HELPER_PROTO, helper->l4proto))
return -1;
return 0;
diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c
index 09475007165b..41c2a0b82a8e 100644
--- a/net/vmw_vsock/virtio_transport_common.c
+++ b/net/vmw_vsock/virtio_transport_common.c
@@ -328,38 +328,35 @@ static int virtio_transport_send_pkt_info(struct vsock_sock *vsk,
if (pkt_len == 0 && info->op == VIRTIO_VSOCK_OP_RW)
return pkt_len;
- if (info->msg) {
- /* If zerocopy is not enabled by 'setsockopt()', we behave as
- * there is no MSG_ZEROCOPY flag set.
+ if (info->msg && (info->msg->msg_flags & MSG_ZEROCOPY)) {
+ /* If 'info->msg' is not NULL, this is only VIRTIO_VSOCK_OP_RW.
+ * 'MSG_ZEROCOPY' flag handling here is based on the same flag
+ * handling from 'tcp_sendmsg_locked()'.
*/
- if (!sock_flag(sk_vsock(vsk), SOCK_ZEROCOPY))
- info->msg->msg_flags &= ~MSG_ZEROCOPY;
+ if (info->msg->msg_ubuf) {
+ uarg = info->msg->msg_ubuf;
+ can_zcopy = virtio_transport_can_zcopy(t_ops, info, pkt_len);
+ } else if (sock_flag(sk_vsock(vsk), SOCK_ZEROCOPY)) {
+ uarg = msg_zerocopy_realloc(sk_vsock(vsk), pkt_len,
+ NULL, false);
+ if (!uarg) {
+ virtio_transport_put_credit(vvs, pkt_len);
+ return -ENOMEM;
+ }
- if (info->msg->msg_flags & MSG_ZEROCOPY)
can_zcopy = virtio_transport_can_zcopy(t_ops, info, pkt_len);
+ if (!can_zcopy)
+ uarg_to_msgzc(uarg)->zerocopy = 0;
+ have_uref = true;
+ }
+
+ /* 'can_zcopy' means that this transmission will be
+ * in zerocopy way (e.g. using 'frags' array).
+ */
if (can_zcopy)
max_skb_len = min_t(u32, VIRTIO_VSOCK_MAX_PKT_BUF_SIZE,
(MAX_SKB_FRAGS * PAGE_SIZE));
-
- if (info->msg->msg_flags & MSG_ZEROCOPY &&
- info->op == VIRTIO_VSOCK_OP_RW) {
- uarg = info->msg->msg_ubuf;
-
- if (!uarg) {
- uarg = msg_zerocopy_realloc(sk_vsock(vsk),
- pkt_len, NULL, false);
- if (!uarg) {
- virtio_transport_put_credit(vvs, pkt_len);
- return -ENOMEM;
- }
-
- if (!can_zcopy)
- uarg_to_msgzc(uarg)->zerocopy = 0;
-
- have_uref = true;
- }
- }
}
rest_len = pkt_len;