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

flow_dissector: Add PPPoE dissectors

Allow to dissect PPPoE specific fields which are:
- session ID (16 bits)
- ppp protocol (16 bits)
- type (16 bits) - this is PPPoE ethertype, for now only
ETH_P_PPP_SES is supported, possible ETH_P_PPP_DISC
in the future

The goal is to make the following TC command possible:

# tc filter add dev ens6f0 ingress prio 1 protocol ppp_ses \
flower \
pppoe_sid 12 \
ppp_proto ip \
action drop

Note that only PPPoE Session is supported.

Signed-off-by: Wojciech Drewek <wojciech.drewek@intel.com>
Acked-by: Guillaume Nault <gnault@redhat.com>
Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>

authored by

Wojciech Drewek and committed by
Tony Nguyen
46126db9 35d099da

+77 -11
+14
include/linux/ppp_defs.h
··· 11 11 #include <uapi/linux/ppp_defs.h> 12 12 13 13 #define PPP_FCS(fcs, c) crc_ccitt_byte(fcs, c) 14 + 15 + /** 16 + * ppp_proto_is_valid - checks if PPP protocol is valid 17 + * @proto: PPP protocol 18 + * 19 + * Assumes proto is not compressed. 20 + * Protocol is valid if the value is odd and the least significant bit of the 21 + * most significant octet is 0 (see RFC 1661, section 2). 22 + */ 23 + static inline bool ppp_proto_is_valid(u16 proto) 24 + { 25 + return !!((proto & 0x0101) == 0x0001); 26 + } 27 + 14 28 #endif /* _PPP_DEFS_H_ */
+13
include/net/flow_dissector.h
··· 277 277 u8 num_of_vlans; 278 278 }; 279 279 280 + /** 281 + * struct flow_dissector_key_pppoe: 282 + * @session_id: pppoe session id 283 + * @ppp_proto: ppp protocol 284 + * @type: pppoe eth type 285 + */ 286 + struct flow_dissector_key_pppoe { 287 + __be16 session_id; 288 + __be16 ppp_proto; 289 + __be16 type; 290 + }; 291 + 280 292 enum flow_dissector_key_id { 281 293 FLOW_DISSECTOR_KEY_CONTROL, /* struct flow_dissector_key_control */ 282 294 FLOW_DISSECTOR_KEY_BASIC, /* struct flow_dissector_key_basic */ ··· 319 307 FLOW_DISSECTOR_KEY_CT, /* struct flow_dissector_key_ct */ 320 308 FLOW_DISSECTOR_KEY_HASH, /* struct flow_dissector_key_hash */ 321 309 FLOW_DISSECTOR_KEY_NUM_OF_VLANS, /* struct flow_dissector_key_num_of_vlans */ 310 + FLOW_DISSECTOR_KEY_PPPOE, /* struct flow_dissector_key_pppoe */ 322 311 323 312 FLOW_DISSECTOR_KEY_MAX, 324 313 };
+50 -11
net/core/flow_dissector.c
··· 895 895 return result == BPF_OK; 896 896 } 897 897 898 + static bool is_pppoe_ses_hdr_valid(struct pppoe_hdr hdr) 899 + { 900 + return hdr.ver == 1 && hdr.type == 1 && hdr.code == 0; 901 + } 902 + 898 903 /** 899 904 * __skb_flow_dissect - extract the flow_keys struct and return it 900 905 * @net: associated network namespace, derived from @skb if NULL ··· 1219 1214 struct pppoe_hdr hdr; 1220 1215 __be16 proto; 1221 1216 } *hdr, _hdr; 1217 + u16 ppp_proto; 1218 + 1222 1219 hdr = __skb_header_pointer(skb, nhoff, sizeof(_hdr), data, hlen, &_hdr); 1223 1220 if (!hdr) { 1224 1221 fdret = FLOW_DISSECT_RET_OUT_BAD; 1225 1222 break; 1226 1223 } 1227 1224 1228 - nhoff += PPPOE_SES_HLEN; 1229 - switch (hdr->proto) { 1230 - case htons(PPP_IP): 1231 - proto = htons(ETH_P_IP); 1232 - fdret = FLOW_DISSECT_RET_PROTO_AGAIN; 1233 - break; 1234 - case htons(PPP_IPV6): 1235 - proto = htons(ETH_P_IPV6); 1236 - fdret = FLOW_DISSECT_RET_PROTO_AGAIN; 1237 - break; 1238 - default: 1225 + if (!is_pppoe_ses_hdr_valid(hdr->hdr)) { 1239 1226 fdret = FLOW_DISSECT_RET_OUT_BAD; 1240 1227 break; 1228 + } 1229 + 1230 + /* least significant bit of the most significant octet 1231 + * indicates if protocol field was compressed 1232 + */ 1233 + ppp_proto = ntohs(hdr->proto); 1234 + if (ppp_proto & 0x0100) { 1235 + ppp_proto = ppp_proto >> 8; 1236 + nhoff += PPPOE_SES_HLEN - 1; 1237 + } else { 1238 + nhoff += PPPOE_SES_HLEN; 1239 + } 1240 + 1241 + if (ppp_proto == PPP_IP) { 1242 + proto = htons(ETH_P_IP); 1243 + fdret = FLOW_DISSECT_RET_PROTO_AGAIN; 1244 + } else if (ppp_proto == PPP_IPV6) { 1245 + proto = htons(ETH_P_IPV6); 1246 + fdret = FLOW_DISSECT_RET_PROTO_AGAIN; 1247 + } else if (ppp_proto == PPP_MPLS_UC) { 1248 + proto = htons(ETH_P_MPLS_UC); 1249 + fdret = FLOW_DISSECT_RET_PROTO_AGAIN; 1250 + } else if (ppp_proto == PPP_MPLS_MC) { 1251 + proto = htons(ETH_P_MPLS_MC); 1252 + fdret = FLOW_DISSECT_RET_PROTO_AGAIN; 1253 + } else if (ppp_proto_is_valid(ppp_proto)) { 1254 + fdret = FLOW_DISSECT_RET_OUT_GOOD; 1255 + } else { 1256 + fdret = FLOW_DISSECT_RET_OUT_BAD; 1257 + break; 1258 + } 1259 + 1260 + if (dissector_uses_key(flow_dissector, 1261 + FLOW_DISSECTOR_KEY_PPPOE)) { 1262 + struct flow_dissector_key_pppoe *key_pppoe; 1263 + 1264 + key_pppoe = skb_flow_dissector_target(flow_dissector, 1265 + FLOW_DISSECTOR_KEY_PPPOE, 1266 + target_container); 1267 + key_pppoe->session_id = hdr->hdr.sid; 1268 + key_pppoe->ppp_proto = htons(ppp_proto); 1269 + key_pppoe->type = htons(ETH_P_PPP_SES); 1241 1270 } 1242 1271 break; 1243 1272 }