[NetLabel]: rework the Netlink attribute handling (part 2)
[linux-2.6.git] / net / netlabel / netlabel_cipso_v4.c
1 /*
2  * NetLabel CIPSO/IPv4 Support
3  *
4  * This file defines the CIPSO/IPv4 functions for the NetLabel system.  The
5  * NetLabel system manages static and dynamic label mappings for network
6  * protocols such as CIPSO and RIPSO.
7  *
8  * Author: Paul Moore <paul.moore@hp.com>
9  *
10  */
11
12 /*
13  * (c) Copyright Hewlett-Packard Development Company, L.P., 2006
14  *
15  * This program is free software;  you can redistribute it and/or modify
16  * it under the terms of the GNU General Public License as published by
17  * the Free Software Foundation; either version 2 of the License, or
18  * (at your option) any later version.
19  *
20  * This program is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY;  without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
23  * the GNU General Public License for more details.
24  *
25  * You should have received a copy of the GNU General Public License
26  * along with this program;  if not, write to the Free Software
27  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28  *
29  */
30
31 #include <linux/types.h>
32 #include <linux/socket.h>
33 #include <linux/string.h>
34 #include <linux/skbuff.h>
35 #include <net/sock.h>
36 #include <net/netlink.h>
37 #include <net/genetlink.h>
38 #include <net/netlabel.h>
39 #include <net/cipso_ipv4.h>
40
41 #include "netlabel_user.h"
42 #include "netlabel_cipso_v4.h"
43
44 /* Argument struct for cipso_v4_doi_walk() */
45 struct netlbl_cipsov4_doiwalk_arg {
46         struct netlink_callback *nl_cb;
47         struct sk_buff *skb;
48         u32 seq;
49 };
50
51 /* NetLabel Generic NETLINK CIPSOv4 family */
52 static struct genl_family netlbl_cipsov4_gnl_family = {
53         .id = GENL_ID_GENERATE,
54         .hdrsize = 0,
55         .name = NETLBL_NLTYPE_CIPSOV4_NAME,
56         .version = NETLBL_PROTO_VERSION,
57         .maxattr = NLBL_CIPSOV4_A_MAX,
58 };
59
60 /* NetLabel Netlink attribute policy */
61 static struct nla_policy netlbl_cipsov4_genl_policy[NLBL_CIPSOV4_A_MAX + 1] = {
62         [NLBL_CIPSOV4_A_DOI] = { .type = NLA_U32 },
63         [NLBL_CIPSOV4_A_MTYPE] = { .type = NLA_U32 },
64         [NLBL_CIPSOV4_A_TAG] = { .type = NLA_U8 },
65         [NLBL_CIPSOV4_A_TAGLST] = { .type = NLA_NESTED },
66         [NLBL_CIPSOV4_A_MLSLVLLOC] = { .type = NLA_U32 },
67         [NLBL_CIPSOV4_A_MLSLVLREM] = { .type = NLA_U32 },
68         [NLBL_CIPSOV4_A_MLSLVL] = { .type = NLA_NESTED },
69         [NLBL_CIPSOV4_A_MLSLVLLST] = { .type = NLA_NESTED },
70         [NLBL_CIPSOV4_A_MLSCATLOC] = { .type = NLA_U32 },
71         [NLBL_CIPSOV4_A_MLSCATREM] = { .type = NLA_U32 },
72         [NLBL_CIPSOV4_A_MLSCAT] = { .type = NLA_NESTED },
73         [NLBL_CIPSOV4_A_MLSCATLST] = { .type = NLA_NESTED },
74 };
75
76 /*
77  * Helper Functions
78  */
79
80 /**
81  * netlbl_cipsov4_doi_free - Frees a CIPSO V4 DOI definition
82  * @entry: the entry's RCU field
83  *
84  * Description:
85  * This function is designed to be used as a callback to the call_rcu()
86  * function so that the memory allocated to the DOI definition can be released
87  * safely.
88  *
89  */
90 static void netlbl_cipsov4_doi_free(struct rcu_head *entry)
91 {
92         struct cipso_v4_doi *ptr;
93
94         ptr = container_of(entry, struct cipso_v4_doi, rcu);
95         switch (ptr->type) {
96         case CIPSO_V4_MAP_STD:
97                 kfree(ptr->map.std->lvl.cipso);
98                 kfree(ptr->map.std->lvl.local);
99                 kfree(ptr->map.std->cat.cipso);
100                 kfree(ptr->map.std->cat.local);
101                 break;
102         }
103         kfree(ptr);
104 }
105
106 /**
107  * netlbl_cipsov4_add_common - Parse the common sections of a ADD message
108  * @info: the Generic NETLINK info block
109  * @doi_def: the CIPSO V4 DOI definition
110  *
111  * Description:
112  * Parse the common sections of a ADD message and fill in the related values
113  * in @doi_def.  Returns zero on success, negative values on failure.
114  *
115  */
116 static int netlbl_cipsov4_add_common(struct genl_info *info,
117                                      struct cipso_v4_doi *doi_def)
118 {
119         struct nlattr *nla;
120         int nla_rem;
121         u32 iter = 0;
122
123         doi_def->doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]);
124
125         if (nla_validate_nested(info->attrs[NLBL_CIPSOV4_A_TAGLST],
126                                 NLBL_CIPSOV4_A_MAX,
127                                 netlbl_cipsov4_genl_policy) != 0)
128                 return -EINVAL;
129
130         nla_for_each_nested(nla, info->attrs[NLBL_CIPSOV4_A_TAGLST], nla_rem)
131                 if (nla->nla_type == NLBL_CIPSOV4_A_TAG) {
132                         if (iter > CIPSO_V4_TAG_MAXCNT)
133                                 return -EINVAL;
134                         doi_def->tags[iter++] = nla_get_u8(nla);
135                 }
136         if (iter < CIPSO_V4_TAG_MAXCNT)
137                 doi_def->tags[iter] = CIPSO_V4_TAG_INVALID;
138
139         return 0;
140 }
141
142 /*
143  * NetLabel Command Handlers
144  */
145
146 /**
147  * netlbl_cipsov4_add_std - Adds a CIPSO V4 DOI definition
148  * @info: the Generic NETLINK info block
149  *
150  * Description:
151  * Create a new CIPSO_V4_MAP_STD DOI definition based on the given ADD message
152  * and add it to the CIPSO V4 engine.  Return zero on success and non-zero on
153  * error.
154  *
155  */
156 static int netlbl_cipsov4_add_std(struct genl_info *info)
157 {
158         int ret_val = -EINVAL;
159         struct cipso_v4_doi *doi_def = NULL;
160         struct nlattr *nla_a;
161         struct nlattr *nla_b;
162         int nla_a_rem;
163         int nla_b_rem;
164
165         if (!info->attrs[NLBL_CIPSOV4_A_DOI] ||
166             !info->attrs[NLBL_CIPSOV4_A_TAGLST] ||
167             !info->attrs[NLBL_CIPSOV4_A_MLSLVLLST])
168                 return -EINVAL;
169
170         if (nla_validate_nested(info->attrs[NLBL_CIPSOV4_A_MLSLVLLST],
171                                 NLBL_CIPSOV4_A_MAX,
172                                 netlbl_cipsov4_genl_policy) != 0)
173                 return -EINVAL;
174
175         doi_def = kmalloc(sizeof(*doi_def), GFP_KERNEL);
176         if (doi_def == NULL)
177                 return -ENOMEM;
178         doi_def->map.std = kzalloc(sizeof(*doi_def->map.std), GFP_KERNEL);
179         if (doi_def->map.std == NULL) {
180                 ret_val = -ENOMEM;
181                 goto add_std_failure;
182         }
183         doi_def->type = CIPSO_V4_MAP_STD;
184
185         ret_val = netlbl_cipsov4_add_common(info, doi_def);
186         if (ret_val != 0)
187                 goto add_std_failure;
188
189         nla_for_each_nested(nla_a,
190                             info->attrs[NLBL_CIPSOV4_A_MLSLVLLST],
191                             nla_a_rem)
192                 if (nla_a->nla_type == NLBL_CIPSOV4_A_MLSLVL) {
193                         nla_for_each_nested(nla_b, nla_a, nla_b_rem)
194                                 switch (nla_b->nla_type) {
195                                 case NLBL_CIPSOV4_A_MLSLVLLOC:
196                                         if (nla_get_u32(nla_b) >=
197                                             doi_def->map.std->lvl.local_size)
198                                              doi_def->map.std->lvl.local_size =
199                                                      nla_get_u32(nla_b) + 1;
200                                         break;
201                                 case NLBL_CIPSOV4_A_MLSLVLREM:
202                                         if (nla_get_u32(nla_b) >=
203                                             doi_def->map.std->lvl.cipso_size)
204                                              doi_def->map.std->lvl.cipso_size =
205                                                      nla_get_u32(nla_b) + 1;
206                                         break;
207                                 }
208                 }
209         if (doi_def->map.std->lvl.local_size > CIPSO_V4_MAX_LOC_LVLS ||
210             doi_def->map.std->lvl.cipso_size > CIPSO_V4_MAX_REM_LVLS)
211                 goto add_std_failure;
212         doi_def->map.std->lvl.local = kcalloc(doi_def->map.std->lvl.local_size,
213                                               sizeof(u32),
214                                               GFP_KERNEL);
215         if (doi_def->map.std->lvl.local == NULL) {
216                 ret_val = -ENOMEM;
217                 goto add_std_failure;
218         }
219         doi_def->map.std->lvl.cipso = kcalloc(doi_def->map.std->lvl.cipso_size,
220                                               sizeof(u32),
221                                               GFP_KERNEL);
222         if (doi_def->map.std->lvl.cipso == NULL) {
223                 ret_val = -ENOMEM;
224                 goto add_std_failure;
225         }
226         nla_for_each_nested(nla_a,
227                             info->attrs[NLBL_CIPSOV4_A_MLSLVLLST],
228                             nla_a_rem)
229                 if (nla_a->nla_type == NLBL_CIPSOV4_A_MLSLVL) {
230                         struct nlattr *lvl_loc;
231                         struct nlattr *lvl_rem;
232
233                         if (nla_validate_nested(nla_a,
234                                               NLBL_CIPSOV4_A_MAX,
235                                               netlbl_cipsov4_genl_policy) != 0)
236                                 goto add_std_failure;
237
238                         lvl_loc = nla_find_nested(nla_a,
239                                                   NLBL_CIPSOV4_A_MLSLVLLOC);
240                         lvl_rem = nla_find_nested(nla_a,
241                                                   NLBL_CIPSOV4_A_MLSLVLREM);
242                         if (lvl_loc == NULL || lvl_rem == NULL)
243                                 goto add_std_failure;
244                         doi_def->map.std->lvl.local[nla_get_u32(lvl_loc)] =
245                                 nla_get_u32(lvl_rem);
246                         doi_def->map.std->lvl.cipso[nla_get_u32(lvl_rem)] =
247                                 nla_get_u32(lvl_loc);
248                 }
249
250         if (info->attrs[NLBL_CIPSOV4_A_MLSCATLST]) {
251                 if (nla_validate_nested(info->attrs[NLBL_CIPSOV4_A_MLSCATLST],
252                                         NLBL_CIPSOV4_A_MAX,
253                                         netlbl_cipsov4_genl_policy) != 0)
254                         goto add_std_failure;
255
256                 nla_for_each_nested(nla_a,
257                                     info->attrs[NLBL_CIPSOV4_A_MLSCATLST],
258                                     nla_a_rem)
259                         if (nla_a->nla_type == NLBL_CIPSOV4_A_MLSCAT) {
260                                 if (nla_validate_nested(nla_a,
261                                               NLBL_CIPSOV4_A_MAX,
262                                               netlbl_cipsov4_genl_policy) != 0)
263                                         goto add_std_failure;
264                                 nla_for_each_nested(nla_b, nla_a, nla_b_rem)
265                                         switch (nla_b->nla_type) {
266                                         case NLBL_CIPSOV4_A_MLSCATLOC:
267                                                 if (nla_get_u32(nla_b) >=
268                                               doi_def->map.std->cat.local_size)
269                                              doi_def->map.std->cat.local_size =
270                                                      nla_get_u32(nla_b) + 1;
271                                                 break;
272                                         case NLBL_CIPSOV4_A_MLSCATREM:
273                                                 if (nla_get_u32(nla_b) >=
274                                               doi_def->map.std->cat.cipso_size)
275                                              doi_def->map.std->cat.cipso_size =
276                                                      nla_get_u32(nla_b) + 1;
277                                                 break;
278                                         }
279                         }
280                 if (doi_def->map.std->cat.local_size > CIPSO_V4_MAX_LOC_CATS ||
281                     doi_def->map.std->cat.cipso_size > CIPSO_V4_MAX_REM_CATS)
282                         goto add_std_failure;
283                 doi_def->map.std->cat.local = kcalloc(
284                                               doi_def->map.std->cat.local_size,
285                                               sizeof(u32),
286                                               GFP_KERNEL);
287                 if (doi_def->map.std->cat.local == NULL) {
288                         ret_val = -ENOMEM;
289                         goto add_std_failure;
290                 }
291                 doi_def->map.std->cat.cipso = kcalloc(
292                                               doi_def->map.std->cat.cipso_size,
293                                               sizeof(u32),
294                                               GFP_KERNEL);
295                 if (doi_def->map.std->cat.cipso == NULL) {
296                         ret_val = -ENOMEM;
297                         goto add_std_failure;
298                 }
299                 nla_for_each_nested(nla_a,
300                                     info->attrs[NLBL_CIPSOV4_A_MLSCATLST],
301                                     nla_a_rem)
302                         if (nla_a->nla_type == NLBL_CIPSOV4_A_MLSCAT) {
303                                 struct nlattr *cat_loc;
304                                 struct nlattr *cat_rem;
305
306                                 cat_loc = nla_find_nested(nla_a,
307                                                      NLBL_CIPSOV4_A_MLSCATLOC);
308                                 cat_rem = nla_find_nested(nla_a,
309                                                      NLBL_CIPSOV4_A_MLSCATREM);
310                                 if (cat_loc == NULL || cat_rem == NULL)
311                                         goto add_std_failure;
312                                 doi_def->map.std->cat.local[
313                                                         nla_get_u32(cat_loc)] =
314                                         nla_get_u32(cat_rem);
315                                 doi_def->map.std->cat.cipso[
316                                                         nla_get_u32(cat_rem)] =
317                                         nla_get_u32(cat_loc);
318                         }
319         }
320
321         ret_val = cipso_v4_doi_add(doi_def);
322         if (ret_val != 0)
323                 goto add_std_failure;
324         return 0;
325
326 add_std_failure:
327         if (doi_def)
328                 netlbl_cipsov4_doi_free(&doi_def->rcu);
329         return ret_val;
330 }
331
332 /**
333  * netlbl_cipsov4_add_pass - Adds a CIPSO V4 DOI definition
334  * @info: the Generic NETLINK info block
335  *
336  * Description:
337  * Create a new CIPSO_V4_MAP_PASS DOI definition based on the given ADD message
338  * and add it to the CIPSO V4 engine.  Return zero on success and non-zero on
339  * error.
340  *
341  */
342 static int netlbl_cipsov4_add_pass(struct genl_info *info)
343 {
344         int ret_val;
345         struct cipso_v4_doi *doi_def = NULL;
346
347         if (!info->attrs[NLBL_CIPSOV4_A_DOI] ||
348             !info->attrs[NLBL_CIPSOV4_A_TAGLST])
349                 return -EINVAL;
350
351         doi_def = kmalloc(sizeof(*doi_def), GFP_KERNEL);
352         if (doi_def == NULL)
353                 return -ENOMEM;
354         doi_def->type = CIPSO_V4_MAP_PASS;
355
356         ret_val = netlbl_cipsov4_add_common(info, doi_def);
357         if (ret_val != 0)
358                 goto add_pass_failure;
359
360         ret_val = cipso_v4_doi_add(doi_def);
361         if (ret_val != 0)
362                 goto add_pass_failure;
363         return 0;
364
365 add_pass_failure:
366         netlbl_cipsov4_doi_free(&doi_def->rcu);
367         return ret_val;
368 }
369
370 /**
371  * netlbl_cipsov4_add - Handle an ADD message
372  * @skb: the NETLINK buffer
373  * @info: the Generic NETLINK info block
374  *
375  * Description:
376  * Create a new DOI definition based on the given ADD message and add it to the
377  * CIPSO V4 engine.  Returns zero on success, negative values on failure.
378  *
379  */
380 static int netlbl_cipsov4_add(struct sk_buff *skb, struct genl_info *info)
381
382 {
383         int ret_val = -EINVAL;
384         u32 map_type;
385
386         if (!info->attrs[NLBL_CIPSOV4_A_MTYPE])
387                 return -EINVAL;
388
389         map_type = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_MTYPE]);
390         switch (map_type) {
391         case CIPSO_V4_MAP_STD:
392                 ret_val = netlbl_cipsov4_add_std(info);
393                 break;
394         case CIPSO_V4_MAP_PASS:
395                 ret_val = netlbl_cipsov4_add_pass(info);
396                 break;
397         }
398
399         return ret_val;
400 }
401
402 /**
403  * netlbl_cipsov4_list - Handle a LIST message
404  * @skb: the NETLINK buffer
405  * @info: the Generic NETLINK info block
406  *
407  * Description:
408  * Process a user generated LIST message and respond accordingly.  While the
409  * response message generated by the kernel is straightforward, determining
410  * before hand the size of the buffer to allocate is not (we have to generate
411  * the message to know the size).  In order to keep this function sane what we
412  * do is allocate a buffer of NLMSG_GOODSIZE and try to fit the response in
413  * that size, if we fail then we restart with a larger buffer and try again.
414  * We continue in this manner until we hit a limit of failed attempts then we
415  * give up and just send an error message.  Returns zero on success and
416  * negative values on error.
417  *
418  */
419 static int netlbl_cipsov4_list(struct sk_buff *skb, struct genl_info *info)
420 {
421         int ret_val;
422         struct sk_buff *ans_skb = NULL;
423         u32 nlsze_mult = 1;
424         void *data;
425         u32 doi;
426         struct nlattr *nla_a;
427         struct nlattr *nla_b;
428         struct cipso_v4_doi *doi_def;
429         u32 iter;
430
431         if (!info->attrs[NLBL_CIPSOV4_A_DOI]) {
432                 ret_val = -EINVAL;
433                 goto list_failure;
434         }
435
436 list_start:
437         ans_skb = nlmsg_new(NLMSG_GOODSIZE * nlsze_mult, GFP_KERNEL);
438         if (ans_skb == NULL) {
439                 ret_val = -ENOMEM;
440                 goto list_failure;
441         }
442         data = netlbl_netlink_hdr_put(ans_skb,
443                                       info->snd_pid,
444                                       info->snd_seq,
445                                       netlbl_cipsov4_gnl_family.id,
446                                       0,
447                                       NLBL_CIPSOV4_C_LIST);
448         if (data == NULL) {
449                 ret_val = -ENOMEM;
450                 goto list_failure;
451         }
452
453         doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]);
454
455         rcu_read_lock();
456         doi_def = cipso_v4_doi_getdef(doi);
457         if (doi_def == NULL) {
458                 ret_val = -EINVAL;
459                 goto list_failure;
460         }
461
462         ret_val = nla_put_u32(ans_skb, NLBL_CIPSOV4_A_MTYPE, doi_def->type);
463         if (ret_val != 0)
464                 goto list_failure_lock;
465
466         nla_a = nla_nest_start(ans_skb, NLBL_CIPSOV4_A_TAGLST);
467         if (nla_a == NULL) {
468                 ret_val = -ENOMEM;
469                 goto list_failure_lock;
470         }
471         for (iter = 0;
472              iter < CIPSO_V4_TAG_MAXCNT &&
473                doi_def->tags[iter] != CIPSO_V4_TAG_INVALID;
474              iter++) {
475                 ret_val = nla_put_u8(ans_skb,
476                                      NLBL_CIPSOV4_A_TAG,
477                                      doi_def->tags[iter]);
478                 if (ret_val != 0)
479                         goto list_failure_lock;
480         }
481         nla_nest_end(ans_skb, nla_a);
482
483         switch (doi_def->type) {
484         case CIPSO_V4_MAP_STD:
485                 nla_a = nla_nest_start(ans_skb, NLBL_CIPSOV4_A_MLSLVLLST);
486                 if (nla_a == NULL) {
487                         ret_val = -ENOMEM;
488                         goto list_failure_lock;
489                 }
490                 for (iter = 0;
491                      iter < doi_def->map.std->lvl.local_size;
492                      iter++) {
493                         if (doi_def->map.std->lvl.local[iter] ==
494                             CIPSO_V4_INV_LVL)
495                                 continue;
496
497                         nla_b = nla_nest_start(ans_skb, NLBL_CIPSOV4_A_MLSLVL);
498                         if (nla_b == NULL) {
499                                 ret_val = -ENOMEM;
500                                 goto list_retry;
501                         }
502                         ret_val = nla_put_u32(ans_skb,
503                                               NLBL_CIPSOV4_A_MLSLVLLOC,
504                                               iter);
505                         if (ret_val != 0)
506                                 goto list_retry;
507                         ret_val = nla_put_u32(ans_skb,
508                                             NLBL_CIPSOV4_A_MLSLVLREM,
509                                             doi_def->map.std->lvl.local[iter]);
510                         if (ret_val != 0)
511                                 goto list_retry;
512                         nla_nest_end(ans_skb, nla_b);
513                 }
514                 nla_nest_end(ans_skb, nla_a);
515
516                 nla_a = nla_nest_start(ans_skb, NLBL_CIPSOV4_A_MLSCATLST);
517                 if (nla_a == NULL) {
518                         ret_val = -ENOMEM;
519                         goto list_retry;
520                 }
521                 for (iter = 0;
522                      iter < doi_def->map.std->cat.local_size;
523                      iter++) {
524                         if (doi_def->map.std->cat.local[iter] ==
525                             CIPSO_V4_INV_CAT)
526                                 continue;
527
528                         nla_b = nla_nest_start(ans_skb, NLBL_CIPSOV4_A_MLSCAT);
529                         if (nla_b == NULL) {
530                                 ret_val = -ENOMEM;
531                                 goto list_retry;
532                         }
533                         ret_val = nla_put_u32(ans_skb,
534                                               NLBL_CIPSOV4_A_MLSCATLOC,
535                                               iter);
536                         if (ret_val != 0)
537                                 goto list_retry;
538                         ret_val = nla_put_u32(ans_skb,
539                                             NLBL_CIPSOV4_A_MLSCATREM,
540                                             doi_def->map.std->cat.local[iter]);
541                         if (ret_val != 0)
542                                 goto list_retry;
543                         nla_nest_end(ans_skb, nla_b);
544                 }
545                 nla_nest_end(ans_skb, nla_a);
546
547                 break;
548         }
549         rcu_read_unlock();
550
551         genlmsg_end(ans_skb, data);
552
553         ret_val = genlmsg_unicast(ans_skb, info->snd_pid);
554         if (ret_val != 0)
555                 goto list_failure;
556
557         return 0;
558
559 list_retry:
560         /* XXX - this limit is a guesstimate */
561         if (nlsze_mult < 4) {
562                 rcu_read_unlock();
563                 kfree_skb(ans_skb);
564                 nlsze_mult++;
565                 goto list_start;
566         }
567 list_failure_lock:
568         rcu_read_unlock();
569 list_failure:
570         kfree_skb(ans_skb);
571         return ret_val;
572 }
573
574 /**
575  * netlbl_cipsov4_listall_cb - cipso_v4_doi_walk() callback for LISTALL
576  * @doi_def: the CIPSOv4 DOI definition
577  * @arg: the netlbl_cipsov4_doiwalk_arg structure
578  *
579  * Description:
580  * This function is designed to be used as a callback to the
581  * cipso_v4_doi_walk() function for use in generating a response for a LISTALL
582  * message.  Returns the size of the message on success, negative values on
583  * failure.
584  *
585  */
586 static int netlbl_cipsov4_listall_cb(struct cipso_v4_doi *doi_def, void *arg)
587 {
588         int ret_val = -ENOMEM;
589         struct netlbl_cipsov4_doiwalk_arg *cb_arg = arg;
590         void *data;
591
592         data = netlbl_netlink_hdr_put(cb_arg->skb,
593                                       NETLINK_CB(cb_arg->nl_cb->skb).pid,
594                                       cb_arg->seq,
595                                       netlbl_cipsov4_gnl_family.id,
596                                       NLM_F_MULTI,
597                                       NLBL_CIPSOV4_C_LISTALL);
598         if (data == NULL)
599                 goto listall_cb_failure;
600
601         ret_val = nla_put_u32(cb_arg->skb, NLBL_CIPSOV4_A_DOI, doi_def->doi);
602         if (ret_val != 0)
603                 goto listall_cb_failure;
604         ret_val = nla_put_u32(cb_arg->skb,
605                               NLBL_CIPSOV4_A_MTYPE,
606                               doi_def->type);
607         if (ret_val != 0)
608                 goto listall_cb_failure;
609
610         return genlmsg_end(cb_arg->skb, data);
611
612 listall_cb_failure:
613         genlmsg_cancel(cb_arg->skb, data);
614         return ret_val;
615 }
616
617 /**
618  * netlbl_cipsov4_listall - Handle a LISTALL message
619  * @skb: the NETLINK buffer
620  * @cb: the NETLINK callback
621  *
622  * Description:
623  * Process a user generated LISTALL message and respond accordingly.  Returns
624  * zero on success and negative values on error.
625  *
626  */
627 static int netlbl_cipsov4_listall(struct sk_buff *skb,
628                                   struct netlink_callback *cb)
629 {
630         struct netlbl_cipsov4_doiwalk_arg cb_arg;
631         int doi_skip = cb->args[0];
632
633         cb_arg.nl_cb = cb;
634         cb_arg.skb = skb;
635         cb_arg.seq = cb->nlh->nlmsg_seq;
636
637         cipso_v4_doi_walk(&doi_skip, netlbl_cipsov4_listall_cb, &cb_arg);
638
639         cb->args[0] = doi_skip;
640         return skb->len;
641 }
642
643 /**
644  * netlbl_cipsov4_remove - Handle a REMOVE message
645  * @skb: the NETLINK buffer
646  * @info: the Generic NETLINK info block
647  *
648  * Description:
649  * Process a user generated REMOVE message and respond accordingly.  Returns
650  * zero on success, negative values on failure.
651  *
652  */
653 static int netlbl_cipsov4_remove(struct sk_buff *skb, struct genl_info *info)
654 {
655         int ret_val = -EINVAL;
656         u32 doi;
657
658         if (info->attrs[NLBL_CIPSOV4_A_DOI]) {
659                 doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]);
660                 ret_val = cipso_v4_doi_remove(doi, netlbl_cipsov4_doi_free);
661         }
662
663         return ret_val;
664 }
665
666 /*
667  * NetLabel Generic NETLINK Command Definitions
668  */
669
670 static struct genl_ops netlbl_cipsov4_genl_c_add = {
671         .cmd = NLBL_CIPSOV4_C_ADD,
672         .flags = GENL_ADMIN_PERM,
673         .policy = netlbl_cipsov4_genl_policy,
674         .doit = netlbl_cipsov4_add,
675         .dumpit = NULL,
676 };
677
678 static struct genl_ops netlbl_cipsov4_genl_c_remove = {
679         .cmd = NLBL_CIPSOV4_C_REMOVE,
680         .flags = GENL_ADMIN_PERM,
681         .policy = netlbl_cipsov4_genl_policy,
682         .doit = netlbl_cipsov4_remove,
683         .dumpit = NULL,
684 };
685
686 static struct genl_ops netlbl_cipsov4_genl_c_list = {
687         .cmd = NLBL_CIPSOV4_C_LIST,
688         .flags = 0,
689         .policy = netlbl_cipsov4_genl_policy,
690         .doit = netlbl_cipsov4_list,
691         .dumpit = NULL,
692 };
693
694 static struct genl_ops netlbl_cipsov4_genl_c_listall = {
695         .cmd = NLBL_CIPSOV4_C_LISTALL,
696         .flags = 0,
697         .policy = netlbl_cipsov4_genl_policy,
698         .doit = NULL,
699         .dumpit = netlbl_cipsov4_listall,
700 };
701
702 /*
703  * NetLabel Generic NETLINK Protocol Functions
704  */
705
706 /**
707  * netlbl_cipsov4_genl_init - Register the CIPSOv4 NetLabel component
708  *
709  * Description:
710  * Register the CIPSOv4 packet NetLabel component with the Generic NETLINK
711  * mechanism.  Returns zero on success, negative values on failure.
712  *
713  */
714 int netlbl_cipsov4_genl_init(void)
715 {
716         int ret_val;
717
718         ret_val = genl_register_family(&netlbl_cipsov4_gnl_family);
719         if (ret_val != 0)
720                 return ret_val;
721
722         ret_val = genl_register_ops(&netlbl_cipsov4_gnl_family,
723                                     &netlbl_cipsov4_genl_c_add);
724         if (ret_val != 0)
725                 return ret_val;
726         ret_val = genl_register_ops(&netlbl_cipsov4_gnl_family,
727                                     &netlbl_cipsov4_genl_c_remove);
728         if (ret_val != 0)
729                 return ret_val;
730         ret_val = genl_register_ops(&netlbl_cipsov4_gnl_family,
731                                     &netlbl_cipsov4_genl_c_list);
732         if (ret_val != 0)
733                 return ret_val;
734         ret_val = genl_register_ops(&netlbl_cipsov4_gnl_family,
735                                     &netlbl_cipsov4_genl_c_listall);
736         if (ret_val != 0)
737                 return ret_val;
738
739         return 0;
740 }