Re: [PATCH nf-next] netfilter: nft_exthdr: add boolean DCCP option matching

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 2023-03-16, at 10:23:34 +0100, Florian Westphal wrote:
> Jeremy Sowden <jeremy@xxxxxxxxxx> wrote:
> > The xt_dccp iptables module supports the matching of DCCP packets based
> > on the presence or absence of DCCP options.  Extend nft_exthdr to add
> > this functionality to nftables.
> > 
> > Link: https://bugzilla.netfilter.org/show_bug.cgi?id=930
> > Signed-off-by: Jeremy Sowden <jeremy@xxxxxxxxxx>
> > ---
> >  include/uapi/linux/netfilter/nf_tables.h |   2 +
> >  net/netfilter/nft_exthdr.c               | 105 +++++++++++++++++++++++
> > +struct nft_exthdr_dccp {
> > +	struct nft_exthdr exthdr;
> > +	/* A buffer into which to copy the DCCP packet options for parsing.  The
> > +	 * options are located between the packet header and its data.  The
> > +	 * offset of the data from the start of the header is stored in an 8-bit
> > +	 * field as the number of 32-bit words, so the options will definitely
> > +	 * be shorter than `4 * U8_MAX` bytes.
> > +	 */
> > +	u8 optbuf[4 * U8_MAX];
> > +};
> > +
> >  static unsigned int optlen(const u8 *opt, unsigned int offset)
> >  {
> >  	/* Beware zero-length options: make finite progress */
> > @@ -406,6 +418,70 @@ static void nft_exthdr_sctp_eval(const struct nft_expr *expr,
> >  		regs->verdict.code = NFT_BREAK;
> >  }
> >  
> > +static void nft_exthdr_dccp_eval(const struct nft_expr *expr,
> > +				 struct nft_regs *regs,
> > +				 const struct nft_pktinfo *pkt)
> > +{
> > +	struct nft_exthdr_dccp *priv_dccp = nft_expr_priv(expr);
> > +	struct nft_exthdr *priv = &priv_dccp->exthdr;
> > +	u32 *dest = &regs->data[priv->dreg];
> > +	unsigned int optoff, optlen, i;
> > +	const struct dccp_hdr *dh;
> > +	struct dccp_hdr _dh;
> > +	const u8 *options;
> > +
> > +	if (pkt->tprot != IPPROTO_DCCP || pkt->fragoff)
> > +		goto err;
> > +
> > +	dh = skb_header_pointer(pkt->skb, nft_thoff(pkt), sizeof(_dh), &_dh);
> > +	if (!dh)
> > +		goto err;
> > +
> > +	if (dh->dccph_doff * 4 < __dccp_hdr_len(dh))
> > +		goto err;
> > +
> > +	optoff = __dccp_hdr_len(dh);
> > +	optlen = dh->dccph_doff * 4 - optoff;
> 
> Perhaps reorder this slightly:
> 
>      optoff = __dccp_hdr_len(dh);
>      if (dh->dccph_doff * 4 <= optoff)
> 	     goto err;
> 
>      optlen = dh->dccph_doff * 4 - optoff;
> 
>      options = skb_header_pointer(pkt->skb, nft_thoff(pkt) + optoff, optlen,
> 				     priv_dccp->optbuf);
> 
> This isn't safe.  priv_dccp->optbuf is neither percpu nor is there
> something that prevents a softinterrupt from firing.
> 
> I suggest you have a look at 'pipapo' set type which uses percpu scratch
> maps.
> 
> Yet another alternative is to provide a small onstack scratch buffer,
> say 256 byte, and fall back to kmalloc for larger spaces.
> 
> Or, always use a on-stack buffer that gets re-used for each of the
> parsed options.

I was trying to avoid an on-stack buffer, 'cause it would be quite big,
while improving concurrency over xt_dccp.c, which has a single file-
scope buffer protected by a spin-lock.  I'll take a look at pipapo.
Thanks for the tips.

> > +	for (i = 0; i < optlen; ) {
> > +		/* Options 0 - 31 are 1B in the length.  Options 32 et seq. are
> > +		 * at least 2B long.  In all cases, the first byte contains the
> > +		 * option type.  In multi-byte options, the second byte contains
> > +		 * the option length, which must be at least two; if it is
> > +		 * greater than two, there are `len - 2` following bytes of
> > +		 * option data.
> > +		 */
> > +		unsigned int len;
> > +
> > +		if (options[i] > 31 && (optlen - i < 2 || options[i + 1] < 2))
> > +			goto err;
> > +
> > +		len = options[i] > 31 ? options[i + 1] : 1;
> > +
> > +		if (optlen - i < len)
> > +			goto err;
> > +
> > +		if (options[i] != priv->type) {
> > +			i += len;
> 
> I think this needs to guard against len == 0?

`len` should be at least 1:

> > +		if (options[i] > 31 && (optlen - i < 2 || options[i + 1] < 2))
> > +			goto err;
> > +

If options[i] > 31, we verify that we can get the length from
options[i + 1] and that it is at least 2.

> > +		len = options[i] > 31 ? options[i + 1] : 1;

If options[i] > 31, we assign options[i + 1], which we know is at
least two, to len; otheriwse, we assign 1.

J.

Attachment: signature.asc
Description: PGP signature


[Index of Archives]     [Netfitler Users]     [Berkeley Packet Filter]     [LARTC]     [Bugtraq]     [Yosemite Forum]

  Powered by Linux