On Tue, Nov 4, 2025 at 6:56 AM Daniel Jurgens <[email protected]> wrote: > > Add support for IP_USER type rules from ethtool. > > Example: > $ ethtool -U ens9 flow-type ip4 src-ip 192.168.51.101 action -1 > Added rule with ID 1 > > The example rule will drop packets with the source IP specified. > > Signed-off-by: Daniel Jurgens <[email protected]> > Reviewed-by: Parav Pandit <[email protected]> > Reviewed-by: Shahar Shitrit <[email protected]> > Reviewed-by: Xuan Zhuo <[email protected]> > --- > v4: > - Fixed bug in protocol check of parse_ip4 > - (u8 *) to (void *) casting. > - Alignment issues. > --- > drivers/net/virtio_net.c | 122 ++++++++++++++++++++++++++++++++++++--- > 1 file changed, 115 insertions(+), 7 deletions(-) > > diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c > index a0e94771a39e..865a27165365 100644 > --- a/drivers/net/virtio_net.c > +++ b/drivers/net/virtio_net.c > @@ -6889,6 +6889,34 @@ static bool validate_eth_mask(const struct virtnet_ff > *ff, > return true; > } > > +static bool validate_ip4_mask(const struct virtnet_ff *ff, > + const struct virtio_net_ff_selector *sel, > + const struct virtio_net_ff_selector *sel_cap) > +{ > + bool partial_mask = !!(sel_cap->flags & > VIRTIO_NET_FF_MASK_F_PARTIAL_MASK); > + struct iphdr *cap, *mask; > + > + cap = (struct iphdr *)&sel_cap->mask; > + mask = (struct iphdr *)&sel->mask; > + > + if (mask->saddr && > + !check_mask_vs_cap(&mask->saddr, &cap->saddr, > + sizeof(__be32), partial_mask)) > + return false; > + > + if (mask->daddr && > + !check_mask_vs_cap(&mask->daddr, &cap->daddr, > + sizeof(__be32), partial_mask)) > + return false; > + > + if (mask->protocol && > + !check_mask_vs_cap(&mask->protocol, &cap->protocol, > + sizeof(u8), partial_mask)) > + return false; > + > + return true; > +} > + > static bool validate_mask(const struct virtnet_ff *ff, > const struct virtio_net_ff_selector *sel) > { > @@ -6900,11 +6928,36 @@ static bool validate_mask(const struct virtnet_ff *ff, > switch (sel->type) { > case VIRTIO_NET_FF_MASK_TYPE_ETH: > return validate_eth_mask(ff, sel, sel_cap); > + > + case VIRTIO_NET_FF_MASK_TYPE_IPV4: > + return validate_ip4_mask(ff, sel, sel_cap); > } > > return false; > } > > +static void parse_ip4(struct iphdr *mask, struct iphdr *key, > + const struct ethtool_rx_flow_spec *fs) > +{ > + const struct ethtool_usrip4_spec *l3_mask = &fs->m_u.usr_ip4_spec; > + const struct ethtool_usrip4_spec *l3_val = &fs->h_u.usr_ip4_spec; > + > + mask->saddr = l3_mask->ip4src; > + mask->daddr = l3_mask->ip4dst; > + key->saddr = l3_val->ip4src; > + key->daddr = l3_val->ip4dst; > + > + if (l3_mask->proto) { > + mask->protocol = l3_mask->proto; > + key->protocol = l3_val->proto; > + } > +} > + > +static bool has_ipv4(u32 flow_type) > +{ > + return flow_type == IP_USER_FLOW; > +} > + > static int setup_classifier(struct virtnet_ff *ff, > struct virtnet_classifier **c) > { > @@ -7039,6 +7092,7 @@ static bool supported_flow_type(const struct > ethtool_rx_flow_spec *fs) > { > switch (fs->flow_type) { > case ETHER_FLOW: > + case IP_USER_FLOW: > return true; > } > > @@ -7067,11 +7121,23 @@ static int validate_flow_input(struct virtnet_ff *ff, > } > > static void calculate_flow_sizes(struct ethtool_rx_flow_spec *fs, > - size_t *key_size, size_t *classifier_size, > - int *num_hdrs) > + size_t *key_size, size_t *classifier_size, > + int *num_hdrs) > { > + size_t size = sizeof(struct ethhdr); > + > *num_hdrs = 1; > *key_size = sizeof(struct ethhdr); > + > + if (fs->flow_type == ETHER_FLOW) > + goto done; > + > + ++(*num_hdrs); > + if (has_ipv4(fs->flow_type)) > + size += sizeof(struct iphdr); > + > +done: > + *key_size = size; > /* > * The classifier size is the size of the classifier header, a > selector > * header for each type of header in the match criteria, and each > header > @@ -7083,8 +7149,9 @@ static void calculate_flow_sizes(struct > ethtool_rx_flow_spec *fs, > } > > static void setup_eth_hdr_key_mask(struct virtio_net_ff_selector *selector, > - u8 *key, > - const struct ethtool_rx_flow_spec *fs) > + u8 *key, > + const struct ethtool_rx_flow_spec *fs, > + int num_hdrs) > { > struct ethhdr *eth_m = (struct ethhdr *)&selector->mask; > struct ethhdr *eth_k = (struct ethhdr *)key; > @@ -7092,8 +7159,33 @@ static void setup_eth_hdr_key_mask(struct > virtio_net_ff_selector *selector, > selector->type = VIRTIO_NET_FF_MASK_TYPE_ETH; > selector->length = sizeof(struct ethhdr); > > - memcpy(eth_m, &fs->m_u.ether_spec, sizeof(*eth_m)); > - memcpy(eth_k, &fs->h_u.ether_spec, sizeof(*eth_k)); > + if (num_hdrs > 1) { > + eth_m->h_proto = cpu_to_be16(0xffff); > + eth_k->h_proto = cpu_to_be16(ETH_P_IP);
Do we need to check IPV6 here? Thanks
