Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net/sched: act_mirred: Pull mac prior redir to non mac_header_xmit device

There's no skb_pull performed when a mirred action is set at egress of a
mac device, with a target device/action that expects skb->data to point
at the network header.

As a result, either the target device is errornously given an skb with
data pointing to the mac (egress case), or the net stack receives the
skb with data pointing to the mac (ingress case).

E.g:
# tc qdisc add dev eth9 root handle 1: prio
# tc filter add dev eth9 parent 1: prio 9 protocol ip handle 9 basic \
action mirred egress redirect dev tun0

(tun0 is a tun device. result: tun0 errornously gets the eth header
instead of the iph)

Revise the push/pull logic of tcf_mirred_act() to not rely on the
skb_at_tc_ingress() vs tcf_mirred_act_wants_ingress() comparison, as it
does not cover all "pull" cases.

Instead, calculate whether the required action on the target device
requires the data to point at the network header, and compare this to
whether skb->data points to network header - and make the push/pull
adjustments as necessary.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Shmulik Ladkani <sladkani@proofpoint.com>
Tested-by: Jamal Hadi Salim <jhs@mojatatu.com>
Acked-by: Jamal Hadi Salim <jhs@mojatatu.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Shmulik Ladkani and committed by
David S. Miller
70cf3dc7 bb3d0b8b

+12 -10
+12 -10
net/sched/act_mirred.c
··· 219 219 bool use_reinsert; 220 220 bool want_ingress; 221 221 bool is_redirect; 222 + bool expects_nh; 222 223 int m_eaction; 223 224 int mac_len; 225 + bool at_nh; 224 226 225 227 rec_level = __this_cpu_inc_return(mirred_rec_level); 226 228 if (unlikely(rec_level > MIRRED_RECURSION_LIMIT)) { ··· 263 261 goto out; 264 262 } 265 263 266 - /* If action's target direction differs than filter's direction, 267 - * and devices expect a mac header on xmit, then mac push/pull is 268 - * needed. 269 - */ 270 264 want_ingress = tcf_mirred_act_wants_ingress(m_eaction); 271 - if (skb_at_tc_ingress(skb) != want_ingress && m_mac_header_xmit) { 272 - if (!skb_at_tc_ingress(skb)) { 273 - /* caught at egress, act ingress: pull mac */ 274 - mac_len = skb_network_header(skb) - skb_mac_header(skb); 265 + 266 + expects_nh = want_ingress || !m_mac_header_xmit; 267 + at_nh = skb->data == skb_network_header(skb); 268 + if (at_nh != expects_nh) { 269 + mac_len = skb_at_tc_ingress(skb) ? skb->mac_len : 270 + skb_network_header(skb) - skb_mac_header(skb); 271 + if (expects_nh) { 272 + /* target device/action expect data at nh */ 275 273 skb_pull_rcsum(skb2, mac_len); 276 274 } else { 277 - /* caught at ingress, act egress: push mac */ 278 - skb_push_rcsum(skb2, skb->mac_len); 275 + /* target device/action expect data at mac */ 276 + skb_push_rcsum(skb2, mac_len); 279 277 } 280 278 } 281 279