Mike Pattrick <[email protected]> writes:
> When conntrack is reassembling packet fragments, the same reassembly
> context can be shared across multiple threads handling different packets
> simultaneously. Once a full packet is assembled, it is added to a packet
> batch for processing, in the case where there are multiple different pmd
> threads accessing conntrack simultaneously, there is a race condition
> where the reassembled packet may be added to an arbitrary batch even if
> the current batch is available.
>
> When this happens, the packet may be handled incorrectly as it is
> inserted into a random openflow execution pipeline, instead of the
> pipeline for that packets flow.
>
> This change makes a best effort attempt to try to add the defragmented
> packet to the current batch. directly. This should succeed most of the
> time.
>
> Fixes: 4ea96698f667 ("Userspace datapath: Add fragmentation handling.")
> Reported-at: https://issues.redhat.com/browse/FDP-560
> Signed-off-by: Mike Pattrick <[email protected]>
> ---
The patch overall looks good to me. I'm considering applying with the
following addition:
diff --git a/utilities/checkpatch.py b/utilities/checkpatch.py
index 6b293770dd..d9b9e0c23f 100755
--- a/utilities/checkpatch.py
+++ b/utilities/checkpatch.py
@@ -63,7 +63,8 @@ def open_spell_check_dict():
'dhcpv6', 'opts', 'metadata', 'geneve', 'mutex',
'netdev', 'netdevs', 'subtable', 'virtio', 'qos',
'policer', 'datapath', 'tunctl', 'attr',
'ethernet',
- 'ether', 'defrag', 'defragment', 'loopback',
'sflow',
+ 'ether', 'defrag', 'defragment', 'defragmented',
+ 'loopback', 'sflow',
'acl', 'initializer', 'recirc', 'xlated',
'unclosed',
'netlink', 'msec', 'usec', 'nsec', 'ms', 'us',
'ns',
'kilobits', 'kbps', 'kilobytes', 'megabytes',
'mbps',
unless anyone objects. This is to squelch:
== Checking 16f6885353c2 ("ipf: Handle common case of ipf defragmentation.") ==
WARNING: Possible misspelled word: "defragmented"
Did you mean: ['defragment ed', 'defragment-ed', 'defragment']
Lines checked: 129, Warnings: 1, Errors: 0
> lib/ipf.c | 27 ++++++++++++++++++++-------
> 1 file changed, 20 insertions(+), 7 deletions(-)
>
> diff --git a/lib/ipf.c b/lib/ipf.c
> index 3c8960be3..2d715f5e9 100644
> --- a/lib/ipf.c
> +++ b/lib/ipf.c
> @@ -506,13 +506,15 @@ ipf_reassemble_v6_frags(struct ipf_list *ipf_list)
> }
>
> /* Called when a frag list state transitions to another state. This is
> - * triggered by new fragment for the list being received.*/
> -static void
> +* triggered by new fragment for the list being received. Returns a
> reassembled
> +* packet if this fragment has completed one. */
> +static struct reassembled_pkt *
> ipf_list_state_transition(struct ipf *ipf, struct ipf_list *ipf_list,
> bool ff, bool lf, bool v6)
> OVS_REQUIRES(ipf->ipf_lock)
> {
> enum ipf_list_state curr_state = ipf_list->state;
> + struct reassembled_pkt *ret = NULL;
> enum ipf_list_state next_state;
> switch (curr_state) {
> case IPF_LIST_STATE_UNUSED:
> @@ -562,12 +564,15 @@ ipf_list_state_transition(struct ipf *ipf, struct
> ipf_list *ipf_list,
> ipf_reassembled_list_add(&ipf->reassembled_pkt_list, rp);
> ipf_expiry_list_remove(ipf_list);
> next_state = IPF_LIST_STATE_COMPLETED;
> + ret = rp;
> } else {
> next_state = IPF_LIST_STATE_REASS_FAIL;
> }
> }
> }
> ipf_list->state = next_state;
> +
> + return ret;
> }
>
> /* Some sanity checks are redundant, but prudent, in case code paths for
> @@ -799,7 +804,8 @@ ipf_is_frag_duped(const struct ipf_frag *frag_list, int
> last_inuse_idx,
> static bool
> ipf_process_frag(struct ipf *ipf, struct ipf_list *ipf_list,
> struct dp_packet *pkt, uint16_t start_data_byte,
> - uint16_t end_data_byte, bool ff, bool lf, bool v6)
> + uint16_t end_data_byte, bool ff, bool lf, bool v6,
> + struct reassembled_pkt **rp)
> OVS_REQUIRES(ipf->ipf_lock)
> {
> bool duped_frag = ipf_is_frag_duped(ipf_list->frag_list,
> @@ -820,7 +826,7 @@ ipf_process_frag(struct ipf *ipf, struct ipf_list
> *ipf_list,
> ipf_list->last_inuse_idx++;
> atomic_count_inc(&ipf->nfrag);
> ipf_count(ipf, v6, IPF_NFRAGS_ACCEPTED);
> - ipf_list_state_transition(ipf, ipf_list, ff, lf, v6);
> + *rp = ipf_list_state_transition(ipf, ipf_list, ff, lf, v6);
> } else {
> OVS_NOT_REACHED();
> }
> @@ -853,7 +859,8 @@ ipf_list_init(struct ipf_list *ipf_list, struct
> ipf_list_key *key,
> * to a list of fragemnts. */
> static bool
> ipf_handle_frag(struct ipf *ipf, struct dp_packet *pkt, ovs_be16 dl_type,
> - uint16_t zone, long long now, uint32_t hash_basis)
> + uint16_t zone, long long now, uint32_t hash_basis,
> + struct reassembled_pkt **rp)
> OVS_REQUIRES(ipf->ipf_lock)
> {
> struct ipf_list_key key;
> @@ -922,7 +929,7 @@ ipf_handle_frag(struct ipf *ipf, struct dp_packet *pkt,
> ovs_be16 dl_type,
> }
>
> return ipf_process_frag(ipf, ipf_list, pkt, start_data_byte,
> - end_data_byte, ff, lf, v6);
> + end_data_byte, ff, lf, v6, rp);
> }
>
> /* Filters out fragments from a batch of fragments and adjust the batch. */
> @@ -941,11 +948,17 @@ ipf_extract_frags_from_batch(struct ipf *ipf, struct
> dp_packet_batch *pb,
> ||
> (dl_type == htons(ETH_TYPE_IPV6) &&
> ipf_is_valid_v6_frag(ipf, pkt)))) {
> + struct reassembled_pkt *rp = NULL;
>
> ovs_mutex_lock(&ipf->ipf_lock);
> - if (!ipf_handle_frag(ipf, pkt, dl_type, zone, now, hash_basis)) {
> + if (!ipf_handle_frag(ipf, pkt, dl_type, zone, now, hash_basis,
> + &rp)) {
> dp_packet_batch_refill(pb, pkt, pb_idx);
> } else {
> + if (rp && !dp_packet_batch_is_full(pb)) {
> + dp_packet_batch_refill(pb, rp->pkt, pb_idx);
> + rp->list->reass_execute_ctx = rp->pkt;
> + }
> dp_packet_delete(pkt);
> }
> ovs_mutex_unlock(&ipf->ipf_lock);
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev