xref: /OK3568_Linux_fs/kernel/net/ife/ife.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  * net/ife/ife.c - Inter-FE protocol based on ForCES WG InterFE LFB
3*4882a593Smuzhiyun  * Copyright (c) 2015 Jamal Hadi Salim <jhs@mojatatu.com>
4*4882a593Smuzhiyun  * Copyright (c) 2017 Yotam Gigi <yotamg@mellanox.com>
5*4882a593Smuzhiyun  *
6*4882a593Smuzhiyun  * Refer to: draft-ietf-forces-interfelfb-03 and netdev01 paper:
7*4882a593Smuzhiyun  * "Distributing Linux Traffic Control Classifier-Action Subsystem"
8*4882a593Smuzhiyun  * Authors: Jamal Hadi Salim and Damascene M. Joachimpillai
9*4882a593Smuzhiyun  *
10*4882a593Smuzhiyun  * This program is free software; you can redistribute it and/or modify
11*4882a593Smuzhiyun  * it under the terms of the GNU General Public License as published by
12*4882a593Smuzhiyun  * the Free Software Foundation.
13*4882a593Smuzhiyun  */
14*4882a593Smuzhiyun 
15*4882a593Smuzhiyun #include <linux/types.h>
16*4882a593Smuzhiyun #include <linux/kernel.h>
17*4882a593Smuzhiyun #include <linux/string.h>
18*4882a593Smuzhiyun #include <linux/errno.h>
19*4882a593Smuzhiyun #include <linux/skbuff.h>
20*4882a593Smuzhiyun #include <linux/rtnetlink.h>
21*4882a593Smuzhiyun #include <linux/module.h>
22*4882a593Smuzhiyun #include <linux/init.h>
23*4882a593Smuzhiyun #include <net/net_namespace.h>
24*4882a593Smuzhiyun #include <net/netlink.h>
25*4882a593Smuzhiyun #include <net/pkt_sched.h>
26*4882a593Smuzhiyun #include <linux/etherdevice.h>
27*4882a593Smuzhiyun #include <net/ife.h>
28*4882a593Smuzhiyun 
29*4882a593Smuzhiyun struct ifeheadr {
30*4882a593Smuzhiyun 	__be16 metalen;
31*4882a593Smuzhiyun 	u8 tlv_data[];
32*4882a593Smuzhiyun };
33*4882a593Smuzhiyun 
ife_encode(struct sk_buff * skb,u16 metalen)34*4882a593Smuzhiyun void *ife_encode(struct sk_buff *skb, u16 metalen)
35*4882a593Smuzhiyun {
36*4882a593Smuzhiyun 	/* OUTERHDR:TOTMETALEN:{TLVHDR:Metadatum:TLVHDR..}:ORIGDATA
37*4882a593Smuzhiyun 	 * where ORIGDATA = original ethernet header ...
38*4882a593Smuzhiyun 	 */
39*4882a593Smuzhiyun 	int hdrm = metalen + IFE_METAHDRLEN;
40*4882a593Smuzhiyun 	int total_push = hdrm + skb->dev->hard_header_len;
41*4882a593Smuzhiyun 	struct ifeheadr *ifehdr;
42*4882a593Smuzhiyun 	struct ethhdr *iethh;	/* inner ether header */
43*4882a593Smuzhiyun 	int skboff = 0;
44*4882a593Smuzhiyun 	int err;
45*4882a593Smuzhiyun 
46*4882a593Smuzhiyun 	err = skb_cow_head(skb, total_push);
47*4882a593Smuzhiyun 	if (unlikely(err))
48*4882a593Smuzhiyun 		return NULL;
49*4882a593Smuzhiyun 
50*4882a593Smuzhiyun 	iethh = (struct ethhdr *) skb->data;
51*4882a593Smuzhiyun 
52*4882a593Smuzhiyun 	__skb_push(skb, total_push);
53*4882a593Smuzhiyun 	memcpy(skb->data, iethh, skb->dev->hard_header_len);
54*4882a593Smuzhiyun 	skb_reset_mac_header(skb);
55*4882a593Smuzhiyun 	skboff += skb->dev->hard_header_len;
56*4882a593Smuzhiyun 
57*4882a593Smuzhiyun 	/* total metadata length */
58*4882a593Smuzhiyun 	ifehdr = (struct ifeheadr *) (skb->data + skboff);
59*4882a593Smuzhiyun 	metalen += IFE_METAHDRLEN;
60*4882a593Smuzhiyun 	ifehdr->metalen = htons(metalen);
61*4882a593Smuzhiyun 
62*4882a593Smuzhiyun 	return ifehdr->tlv_data;
63*4882a593Smuzhiyun }
64*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(ife_encode);
65*4882a593Smuzhiyun 
ife_decode(struct sk_buff * skb,u16 * metalen)66*4882a593Smuzhiyun void *ife_decode(struct sk_buff *skb, u16 *metalen)
67*4882a593Smuzhiyun {
68*4882a593Smuzhiyun 	struct ifeheadr *ifehdr;
69*4882a593Smuzhiyun 	int total_pull;
70*4882a593Smuzhiyun 	u16 ifehdrln;
71*4882a593Smuzhiyun 
72*4882a593Smuzhiyun 	if (!pskb_may_pull(skb, skb->dev->hard_header_len + IFE_METAHDRLEN))
73*4882a593Smuzhiyun 		return NULL;
74*4882a593Smuzhiyun 
75*4882a593Smuzhiyun 	ifehdr = (struct ifeheadr *) (skb->data + skb->dev->hard_header_len);
76*4882a593Smuzhiyun 	ifehdrln = ntohs(ifehdr->metalen);
77*4882a593Smuzhiyun 	total_pull = skb->dev->hard_header_len + ifehdrln;
78*4882a593Smuzhiyun 
79*4882a593Smuzhiyun 	if (unlikely(ifehdrln < 2))
80*4882a593Smuzhiyun 		return NULL;
81*4882a593Smuzhiyun 
82*4882a593Smuzhiyun 	if (unlikely(!pskb_may_pull(skb, total_pull)))
83*4882a593Smuzhiyun 		return NULL;
84*4882a593Smuzhiyun 
85*4882a593Smuzhiyun 	skb_set_mac_header(skb, total_pull);
86*4882a593Smuzhiyun 	__skb_pull(skb, total_pull);
87*4882a593Smuzhiyun 	*metalen = ifehdrln - IFE_METAHDRLEN;
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun 	return &ifehdr->tlv_data;
90*4882a593Smuzhiyun }
91*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(ife_decode);
92*4882a593Smuzhiyun 
93*4882a593Smuzhiyun struct meta_tlvhdr {
94*4882a593Smuzhiyun 	__be16 type;
95*4882a593Smuzhiyun 	__be16 len;
96*4882a593Smuzhiyun };
97*4882a593Smuzhiyun 
__ife_tlv_meta_valid(const unsigned char * skbdata,const unsigned char * ifehdr_end)98*4882a593Smuzhiyun static bool __ife_tlv_meta_valid(const unsigned char *skbdata,
99*4882a593Smuzhiyun 				 const unsigned char *ifehdr_end)
100*4882a593Smuzhiyun {
101*4882a593Smuzhiyun 	const struct meta_tlvhdr *tlv;
102*4882a593Smuzhiyun 	u16 tlvlen;
103*4882a593Smuzhiyun 
104*4882a593Smuzhiyun 	if (unlikely(skbdata + sizeof(*tlv) > ifehdr_end))
105*4882a593Smuzhiyun 		return false;
106*4882a593Smuzhiyun 
107*4882a593Smuzhiyun 	tlv = (const struct meta_tlvhdr *)skbdata;
108*4882a593Smuzhiyun 	tlvlen = ntohs(tlv->len);
109*4882a593Smuzhiyun 
110*4882a593Smuzhiyun 	/* tlv length field is inc header, check on minimum */
111*4882a593Smuzhiyun 	if (tlvlen < NLA_HDRLEN)
112*4882a593Smuzhiyun 		return false;
113*4882a593Smuzhiyun 
114*4882a593Smuzhiyun 	/* overflow by NLA_ALIGN check */
115*4882a593Smuzhiyun 	if (NLA_ALIGN(tlvlen) < tlvlen)
116*4882a593Smuzhiyun 		return false;
117*4882a593Smuzhiyun 
118*4882a593Smuzhiyun 	if (unlikely(skbdata + NLA_ALIGN(tlvlen) > ifehdr_end))
119*4882a593Smuzhiyun 		return false;
120*4882a593Smuzhiyun 
121*4882a593Smuzhiyun 	return true;
122*4882a593Smuzhiyun }
123*4882a593Smuzhiyun 
124*4882a593Smuzhiyun /* Caller takes care of presenting data in network order
125*4882a593Smuzhiyun  */
ife_tlv_meta_decode(void * skbdata,const void * ifehdr_end,u16 * attrtype,u16 * dlen,u16 * totlen)126*4882a593Smuzhiyun void *ife_tlv_meta_decode(void *skbdata, const void *ifehdr_end, u16 *attrtype,
127*4882a593Smuzhiyun 			  u16 *dlen, u16 *totlen)
128*4882a593Smuzhiyun {
129*4882a593Smuzhiyun 	struct meta_tlvhdr *tlv;
130*4882a593Smuzhiyun 
131*4882a593Smuzhiyun 	if (!__ife_tlv_meta_valid(skbdata, ifehdr_end))
132*4882a593Smuzhiyun 		return NULL;
133*4882a593Smuzhiyun 
134*4882a593Smuzhiyun 	tlv = (struct meta_tlvhdr *)skbdata;
135*4882a593Smuzhiyun 	*dlen = ntohs(tlv->len) - NLA_HDRLEN;
136*4882a593Smuzhiyun 	*attrtype = ntohs(tlv->type);
137*4882a593Smuzhiyun 
138*4882a593Smuzhiyun 	if (totlen)
139*4882a593Smuzhiyun 		*totlen = nla_total_size(*dlen);
140*4882a593Smuzhiyun 
141*4882a593Smuzhiyun 	return skbdata + sizeof(struct meta_tlvhdr);
142*4882a593Smuzhiyun }
143*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(ife_tlv_meta_decode);
144*4882a593Smuzhiyun 
ife_tlv_meta_next(void * skbdata)145*4882a593Smuzhiyun void *ife_tlv_meta_next(void *skbdata)
146*4882a593Smuzhiyun {
147*4882a593Smuzhiyun 	struct meta_tlvhdr *tlv = (struct meta_tlvhdr *) skbdata;
148*4882a593Smuzhiyun 	u16 tlvlen = ntohs(tlv->len);
149*4882a593Smuzhiyun 
150*4882a593Smuzhiyun 	tlvlen = NLA_ALIGN(tlvlen);
151*4882a593Smuzhiyun 
152*4882a593Smuzhiyun 	return skbdata + tlvlen;
153*4882a593Smuzhiyun }
154*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(ife_tlv_meta_next);
155*4882a593Smuzhiyun 
156*4882a593Smuzhiyun /* Caller takes care of presenting data in network order
157*4882a593Smuzhiyun  */
ife_tlv_meta_encode(void * skbdata,u16 attrtype,u16 dlen,const void * dval)158*4882a593Smuzhiyun int ife_tlv_meta_encode(void *skbdata, u16 attrtype, u16 dlen, const void *dval)
159*4882a593Smuzhiyun {
160*4882a593Smuzhiyun 	__be32 *tlv = (__be32 *) (skbdata);
161*4882a593Smuzhiyun 	u16 totlen = nla_total_size(dlen);	/*alignment + hdr */
162*4882a593Smuzhiyun 	char *dptr = (char *) tlv + NLA_HDRLEN;
163*4882a593Smuzhiyun 	u32 htlv = attrtype << 16 | (dlen + NLA_HDRLEN);
164*4882a593Smuzhiyun 
165*4882a593Smuzhiyun 	*tlv = htonl(htlv);
166*4882a593Smuzhiyun 	memset(dptr, 0, totlen - NLA_HDRLEN);
167*4882a593Smuzhiyun 	memcpy(dptr, dval, dlen);
168*4882a593Smuzhiyun 
169*4882a593Smuzhiyun 	return totlen;
170*4882a593Smuzhiyun }
171*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(ife_tlv_meta_encode);
172*4882a593Smuzhiyun 
173*4882a593Smuzhiyun MODULE_AUTHOR("Jamal Hadi Salim <jhs@mojatatu.com>");
174*4882a593Smuzhiyun MODULE_AUTHOR("Yotam Gigi <yotam.gi@gmail.com>");
175*4882a593Smuzhiyun MODULE_DESCRIPTION("Inter-FE LFB action");
176*4882a593Smuzhiyun MODULE_LICENSE("GPL");
177