3333
3434#include "core.h"
3535#include "config.h"
36+ #include "bearer.h"
3637#include <net/genetlink.h>
3738#include <linux/tipc_config.h>
3839
40+ /* The legacy API had an artificial message length limit called
41+ * ULTRA_STRING_MAX_LEN.
42+ */
43+ #define ULTRA_STRING_MAX_LEN 32768
44+
45+ #define TIPC_SKB_MAX TLV_SPACE(ULTRA_STRING_MAX_LEN)
46+
47+ #define REPLY_TRUNCATED "<truncated>\n"
48+
49+ struct tipc_nl_compat_msg {
50+ u16 cmd ;
51+ int rep_size ;
52+ struct sk_buff * rep ;
53+ struct tlv_desc * req ;
54+ struct sock * dst_sk ;
55+ };
56+
57+ struct tipc_nl_compat_cmd_dump {
58+ int (* dumpit )(struct sk_buff * , struct netlink_callback * );
59+ int (* format )(struct tipc_nl_compat_msg * msg , struct nlattr * * attrs );
60+ };
61+
62+ static int tipc_skb_tailroom (struct sk_buff * skb )
63+ {
64+ int tailroom ;
65+ int limit ;
66+
67+ tailroom = skb_tailroom (skb );
68+ limit = TIPC_SKB_MAX - skb -> len ;
69+
70+ if (tailroom < limit )
71+ return tailroom ;
72+
73+ return limit ;
74+ }
75+
76+ static int tipc_add_tlv (struct sk_buff * skb , u16 type , void * data , u16 len )
77+ {
78+ struct tlv_desc * tlv = (struct tlv_desc * )skb_tail_pointer (skb );
79+
80+ if (tipc_skb_tailroom (skb ) < TLV_SPACE (len ))
81+ return - EMSGSIZE ;
82+
83+ skb_put (skb , TLV_SPACE (len ));
84+ tlv -> tlv_type = htons (type );
85+ tlv -> tlv_len = htons (TLV_LENGTH (len ));
86+ if (len && data )
87+ memcpy (TLV_DATA (tlv ), data , len );
88+
89+ return 0 ;
90+ }
91+
92+ static struct sk_buff * tipc_tlv_alloc (int size )
93+ {
94+ int hdr_len ;
95+ struct sk_buff * buf ;
96+
97+ size = TLV_SPACE (size );
98+ hdr_len = nlmsg_total_size (GENL_HDRLEN + TIPC_GENL_HDRLEN );
99+
100+ buf = alloc_skb (hdr_len + size , GFP_KERNEL );
101+ if (!buf )
102+ return NULL ;
103+
104+ skb_reserve (buf , hdr_len );
105+
106+ return buf ;
107+ }
108+
109+ static struct sk_buff * tipc_get_err_tlv (char * str )
110+ {
111+ int str_len = strlen (str ) + 1 ;
112+ struct sk_buff * buf ;
113+
114+ buf = tipc_tlv_alloc (TLV_SPACE (str_len ));
115+ if (buf )
116+ tipc_add_tlv (buf , TIPC_TLV_ERROR_STRING , str , str_len );
117+
118+ return buf ;
119+ }
120+
121+ static int __tipc_nl_compat_dumpit (struct tipc_nl_compat_cmd_dump * cmd ,
122+ struct tipc_nl_compat_msg * msg ,
123+ struct sk_buff * arg )
124+ {
125+ int len = 0 ;
126+ int err ;
127+ struct sk_buff * buf ;
128+ struct nlmsghdr * nlmsg ;
129+ struct netlink_callback cb ;
130+
131+ memset (& cb , 0 , sizeof (cb ));
132+ cb .nlh = (struct nlmsghdr * )arg -> data ;
133+ cb .skb = arg ;
134+
135+ buf = nlmsg_new (NLMSG_GOODSIZE , GFP_KERNEL );
136+ if (!buf )
137+ return - ENOMEM ;
138+
139+ buf -> sk = msg -> dst_sk ;
140+
141+ do {
142+ int rem ;
143+
144+ len = (* cmd -> dumpit )(buf , & cb );
145+
146+ nlmsg_for_each_msg (nlmsg , nlmsg_hdr (buf ), len , rem ) {
147+ struct nlattr * * attrs ;
148+
149+ err = tipc_nlmsg_parse (nlmsg , & attrs );
150+ if (err )
151+ goto err_out ;
152+
153+ err = (* cmd -> format )(msg , attrs );
154+ if (err )
155+ goto err_out ;
156+
157+ if (tipc_skb_tailroom (msg -> rep ) <= 1 ) {
158+ err = - EMSGSIZE ;
159+ goto err_out ;
160+ }
161+ }
162+
163+ skb_reset_tail_pointer (buf );
164+ buf -> len = 0 ;
165+
166+ } while (len );
167+
168+ err = 0 ;
169+
170+ err_out :
171+ kfree_skb (buf );
172+
173+ if (err == - EMSGSIZE ) {
174+ /* The legacy API only considered messages filling
175+ * "ULTRA_STRING_MAX_LEN" to be truncated.
176+ */
177+ if ((TIPC_SKB_MAX - msg -> rep -> len ) <= 1 ) {
178+ char * tail = skb_tail_pointer (msg -> rep );
179+
180+ if (* tail != '\0' )
181+ sprintf (tail - sizeof (REPLY_TRUNCATED ) - 1 ,
182+ REPLY_TRUNCATED );
183+ }
184+
185+ return 0 ;
186+ }
187+
188+ return err ;
189+ }
190+
191+ static int tipc_nl_compat_dumpit (struct tipc_nl_compat_cmd_dump * cmd ,
192+ struct tipc_nl_compat_msg * msg )
193+ {
194+ int err ;
195+ struct sk_buff * arg ;
196+
197+ msg -> rep = tipc_tlv_alloc (msg -> rep_size );
198+ if (!msg -> rep )
199+ return - ENOMEM ;
200+
201+ arg = nlmsg_new (0 , GFP_KERNEL );
202+ if (!arg ) {
203+ kfree_skb (msg -> rep );
204+ return - ENOMEM ;
205+ }
206+
207+ err = __tipc_nl_compat_dumpit (cmd , msg , arg );
208+ if (err )
209+ kfree_skb (msg -> rep );
210+
211+ kfree_skb (arg );
212+
213+ return err ;
214+ }
215+
216+ static int tipc_nl_compat_bearer_dump (struct tipc_nl_compat_msg * msg ,
217+ struct nlattr * * attrs )
218+ {
219+ struct nlattr * bearer [TIPC_NLA_BEARER_MAX + 1 ];
220+
221+ nla_parse_nested (bearer , TIPC_NLA_BEARER_MAX , attrs [TIPC_NLA_BEARER ],
222+ NULL );
223+
224+ return tipc_add_tlv (msg -> rep , TIPC_TLV_BEARER_NAME ,
225+ nla_data (bearer [TIPC_NLA_BEARER_NAME ]),
226+ nla_len (bearer [TIPC_NLA_BEARER_NAME ]));
227+ }
228+
229+ static int tipc_nl_compat_handle (struct tipc_nl_compat_msg * msg )
230+ {
231+ struct tipc_nl_compat_cmd_dump dump ;
232+
233+ memset (& dump , 0 , sizeof (dump ));
234+
235+ switch (msg -> cmd ) {
236+ case TIPC_CMD_GET_BEARER_NAMES :
237+ msg -> rep_size = MAX_BEARERS * TLV_SPACE (TIPC_MAX_BEARER_NAME );
238+ dump .dumpit = tipc_nl_bearer_dump ;
239+ dump .format = tipc_nl_compat_bearer_dump ;
240+ return tipc_nl_compat_dumpit (& dump , msg );
241+ }
242+
243+ return - EOPNOTSUPP ;
244+ }
245+
246+ static int tipc_nl_compat_recv (struct sk_buff * skb , struct genl_info * info )
247+ {
248+ int err ;
249+ int len ;
250+ struct tipc_nl_compat_msg msg ;
251+ struct nlmsghdr * req_nlh ;
252+ struct nlmsghdr * rep_nlh ;
253+ struct tipc_genlmsghdr * req_userhdr = info -> userhdr ;
254+ struct net * net = genl_info_net (info );
255+
256+ memset (& msg , 0 , sizeof (msg ));
257+
258+ req_nlh = (struct nlmsghdr * )skb -> data ;
259+ msg .req = nlmsg_data (req_nlh ) + GENL_HDRLEN + TIPC_GENL_HDRLEN ;
260+ msg .cmd = req_userhdr -> cmd ;
261+ msg .dst_sk = info -> dst_sk ;
262+
263+ if ((msg .cmd & 0xC000 ) && (!netlink_net_capable (skb , CAP_NET_ADMIN ))) {
264+ msg .rep = tipc_get_err_tlv (TIPC_CFG_NOT_NET_ADMIN );
265+ err = - EACCES ;
266+ goto send ;
267+ }
268+
269+ len = nlmsg_attrlen (req_nlh , GENL_HDRLEN + TIPC_GENL_HDRLEN );
270+ if (TLV_GET_LEN (msg .req ) && !TLV_OK (msg .req , len )) {
271+ msg .rep = tipc_get_err_tlv (TIPC_CFG_NOT_SUPPORTED );
272+ err = - EOPNOTSUPP ;
273+ goto send ;
274+ }
275+
276+ err = tipc_nl_compat_handle (& msg );
277+ if (err == - EOPNOTSUPP )
278+ msg .rep = tipc_get_err_tlv (TIPC_CFG_NOT_SUPPORTED );
279+ else if (err == - EINVAL )
280+ msg .rep = tipc_get_err_tlv (TIPC_CFG_TLV_ERROR );
281+ send :
282+ if (!msg .rep )
283+ return err ;
284+
285+ len = nlmsg_total_size (GENL_HDRLEN + TIPC_GENL_HDRLEN );
286+ skb_push (msg .rep , len );
287+ rep_nlh = nlmsg_hdr (msg .rep );
288+ memcpy (rep_nlh , info -> nlhdr , len );
289+ rep_nlh -> nlmsg_len = msg .rep -> len ;
290+ genlmsg_unicast (net , msg .rep , NETLINK_CB (skb ).portid );
291+
292+ return err ;
293+ }
294+
39295static int handle_cmd (struct sk_buff * skb , struct genl_info * info )
40296{
41297 struct net * net = genl_info_net (info );
@@ -69,6 +325,22 @@ static int handle_cmd(struct sk_buff *skb, struct genl_info *info)
69325 return 0 ;
70326}
71327
328+ /* Temporary function to keep functionality throughout the patchset
329+ * without having to mess with the global variables and other trickery
330+ * of the old API.
331+ */
332+ static int tipc_nl_compat_tmp_wrap (struct sk_buff * skb , struct genl_info * info )
333+ {
334+ struct tipc_genlmsghdr * req = info -> userhdr ;
335+
336+ switch (req -> cmd ) {
337+ case TIPC_CMD_GET_BEARER_NAMES :
338+ return tipc_nl_compat_recv (skb , info );
339+ }
340+
341+ return handle_cmd (skb , info );
342+ }
343+
72344static struct genl_family tipc_genl_compat_family = {
73345 .id = GENL_ID_GENERATE ,
74346 .name = TIPC_GENL_NAME ,
@@ -81,7 +353,7 @@ static struct genl_family tipc_genl_compat_family = {
81353static struct genl_ops tipc_genl_compat_ops [] = {
82354 {
83355 .cmd = TIPC_GENL_CMD ,
84- .doit = handle_cmd ,
356+ .doit = tipc_nl_compat_tmp_wrap ,
85357 },
86358};
87359
0 commit comments