On Mon, Aug 20, 2018 at 1:47 AM Song Liu <[email protected]> wrote:
>
> On Thu, Aug 16, 2018 at 4:14 PM, Petar Penkov <[email protected]> wrote:
> > On Thu, Aug 16, 2018 at 3:40 PM, Song Liu <[email protected]> wrote:
> >>
> >> On Thu, Aug 16, 2018 at 9:44 AM, Petar Penkov <[email protected]>
> >> wrote:
> >> > From: Petar Penkov <[email protected]>
> >> >
> >> > Adds a hook for programs of type BPF_PROG_TYPE_FLOW_DISSECTOR and
> >> > attach type BPF_FLOW_DISSECTOR that is executed in the flow dissector
> >> > path. The BPF program is kept as a global variable so it is
> >> > accessible to all flow dissectors.
> >> >
> >> > Signed-off-by: Petar Penkov <[email protected]>
> >> > Signed-off-by: Willem de Bruijn <[email protected]>
> >> > @@ -658,6 +698,42 @@ bool __skb_flow_dissect(const struct sk_buff *skb,
> >> > FLOW_DISSECTOR_KEY_BASIC,
> >> > target_container);
> >> >
> >> > + rcu_read_lock();
> >> > + attached = rcu_dereference(flow_dissector_prog);
> >> > + if (attached) {
> >> > + /* Note that even though the const qualifier is discarded
> >> > + * throughout the execution of the BPF program, all
> >> > changes(the
> >> > + * control block) are reverted after the BPF program
> >> > returns.
> >> > + * Therefore, __skb_flow_dissect does not alter the skb.
> >> > + */
> >> > + struct bpf_flow_dissect_cb *cb;
> >> > + u8 cb_saved[BPF_SKB_CB_LEN];
> >> > + u32 result;
> >> > +
> >> > + cb = (struct bpf_flow_dissect_cb *)(bpf_skb_cb((struct
> >> > sk_buff *)skb));
> >> > +
> >> > + /* Save Control Block */
> >> > + memcpy(cb_saved, cb, sizeof(cb_saved));
> >> > + memset(cb, 0, sizeof(cb_saved));
> >> > +
> >> > + /* Pass parameters to the BPF program */
> >> > + cb->nhoff = nhoff;
> >> > + cb->target_container = target_container;
> >> > + cb->flow_dissector = flow_dissector;
> >> > +
> >> > + bpf_compute_data_pointers((struct sk_buff *)skb);
> >> > + result = BPF_PROG_RUN(attached, skb);
> >> > +
> >> > + /* Restore state */
> >> > + memcpy(cb, cb_saved, sizeof(cb_saved));
> >> > +
> >> > + key_control->thoff = min_t(u16, key_control->thoff,
> >> > + skb ? skb->len : hlen);
> >> > + rcu_read_unlock();
> >> > + return result == BPF_OK;
> >> > + }
> >>
> >> If the BPF program cannot handle certain protocol, shall we fall back
> >> to the built-in logic? Otherwise, all BPF programs need to have some
> >> code for all protocols.
> >>
> >> Song
> >
> > I believe that if we fall back to the built-in logic we lose all security
> > guarantees from BPF and this is why the code does not support
> > fall back.
> >
> > Petar
>
> I am not really sure we are on the same page. I am proposing 3
> different return values from BPF_PROG_RUN(), and they should be
> handled as
>
> 1. result == BPF_OK => return true;
> 2. result == BPF_DROP => return false;
> 3. result == something else => fall back.
>
> Does this proposal make any sense?
>
> Thanks,
> Song
It certainly makes sense. We debated it initially, as well.
In the short term, it allows for simpler BPF programs, as they can
off-load some protocols to the C implementation.
But the RFC patchset already implements most protocols in BPF.
I had not expected that when we started out.
Eventually, I think it is preferable to just deprecate the C
implementation. Which is not possible if we make this opt-out
a part of the BPF flow dissector interface.
There is also the lesser issue that a buggy BPF program might
accidentally pass the third value and unknowing open itself up
to the large attack surface. Without this option, the security
audit is much simpler.