2 * IGMP Snooping layer linux specific code
4 * Copyright (C) 2009, Broadcom Corporation
7 * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom Corporation;
8 * the contents of this file may not be disclosed to third parties, copied
9 * or duplicated in any form, in whole or in part, without the prior
10 * written permission of Broadcom Corporation.
12 * $Id: igs_linux.c,v 1.7 2009/04/28 00:08:23 Exp $
14 #include <linux/module.h>
15 #include <linux/netdevice.h>
16 #include <linux/proc_fs.h>
17 #include <linux/netlink.h>
20 #include <proto/ethernet.h>
24 #include <emf/igs/osl_linux.h>
25 #include <emf/igs/igs_cfg.h>
26 #include <emf/igs/igsc_export.h>
27 #include "igs_linux.h"
29 MODULE_LICENSE("Proprietary");
31 static igs_struct_t igs
;
34 * Description: This function is called by IGS Common code when it wants
35 * to send a packet on to all the LAN ports. It allocates
36 * the native OS packet buffer, adds mac header and forwards
37 * a copy of frame on to LAN ports.
39 * Input: igs_info - IGS instance information.
40 * ip - Pointer to the buffer containing the frame to
42 * length - Length of the buffer.
43 * mgrp_ip - Multicast destination address.
45 * Return: SUCCESS or FAILURE
48 igs_broadcast(igs_info_t
*igs_info
, uint8
*ip
, uint32 length
, uint32 mgrp_ip
)
51 struct net_device
*br_dev
;
52 struct ether_header
*eh
;
54 br_dev
= igs_info
->br_dev
;
58 if ((br_dev
->flags
& IFF_UP
) == 0)
60 IGS_ERROR("Bridge interface %s is down\n", br_dev
->name
);
64 skb
= dev_alloc_skb(length
+ ETHER_HDR_LEN
);
68 IGS_ERROR("Out of memory allocating IGMP Query packet\n");
72 IGS_DEBUG("Allocated pkt buffer for IGMP Query\n");
74 skb_pull(skb
, ETHER_HDR_LEN
);
75 memcpy(skb
->data
, ip
, length
);
78 /* Add the ethernet header */
79 eh
= (struct ether_header
*)skb_push(skb
, ETH_HLEN
);
80 eh
->ether_type
= __constant_htons(ETH_P_IP
);
81 eh
->ether_dhost
[0] = 0x01;
82 eh
->ether_dhost
[1] = 0x00;
83 eh
->ether_dhost
[2] = 0x5e;
84 eh
->ether_dhost
[5] = mgrp_ip
& 0xff; mgrp_ip
>>= 8;
85 eh
->ether_dhost
[4] = mgrp_ip
& 0xff; mgrp_ip
>>= 8;
86 eh
->ether_dhost
[3] = mgrp_ip
& 0x7f;
88 /* Send the frame on to the bridge device */
89 memcpy(eh
->ether_shost
, br_dev
->dev_addr
, br_dev
->addr_len
);
90 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22)
91 skb_reset_mac_header(skb
);
93 skb
->mac
.raw
= skb
->data
;
98 IGS_DEBUG("IGMP Query sent on %s\n", br_dev
->name
);
103 #ifdef CONFIG_PROC_FS
105 * IGSL Packet Counters/Statistics Function
108 igs_stats_get(char *buf
, char **start
, off_t offset
, int32 size
,
109 int32
*eof
, void *data
)
111 igs_info_t
*igs_info
;
112 igs_cfg_request_t cfg
;
116 igs_info
= (igs_info_t
*)data
;
118 strcpy(cfg
.inst_id
, igs_info
->inst_id
);
119 cfg
.command_id
= IGSCFG_CMD_IGS_STATS
;
120 cfg
.oper_type
= IGSCFG_OPER_TYPE_GET
;
121 cfg
.size
= sizeof(cfg
.arg
);
122 stats
= (igs_stats_t
*)cfg
.arg
;
124 igsc_cfg_request_process(igs_info
->igsc_info
, &cfg
);
125 if (cfg
.status
!= IGSCFG_STATUS_SUCCESS
)
127 IGS_ERROR("Unable to get the IGS stats\n");
131 bcm_binit(&b
, buf
, size
);
132 bcm_bprintf(&b
, "IgmpPkts IgmpQueries "
133 "IgmpReports IgmpV2Reports IgmpLeaves\n");
134 bcm_bprintf(&b
, "%-15d %-15d %-15d %-15d %d\n",
135 stats
->igmp_packets
, stats
->igmp_queries
,
136 stats
->igmp_reports
, stats
->igmp_v2reports
,
138 bcm_bprintf(&b
, "IgmpNotHandled McastGroups "
139 "McastMembers MemTimeouts\n");
140 bcm_bprintf(&b
, "%-15d %-15d %-15d %d\n",
141 stats
->igmp_not_handled
, stats
->igmp_mcast_groups
,
142 stats
->igmp_mcast_members
, stats
->igmp_mem_timeouts
);
146 IGS_ERROR("Input buffer overflow\n");
150 return (b
.buf
- b
.origbuf
);
154 igs_sdb_list(char *buf
, char **start
, off_t offset
, int32 size
,
155 int32
*eof
, void *data
)
157 igs_info_t
*igs_info
;
158 igs_cfg_request_t cfg
;
159 igs_cfg_sdb_list_t
*list
;
163 igs_info
= (igs_info_t
*)data
;
165 strcpy(cfg
.inst_id
, igs_info
->inst_id
);
166 cfg
.command_id
= IGSCFG_CMD_IGSDB_LIST
;
167 cfg
.oper_type
= IGSCFG_OPER_TYPE_GET
;
168 cfg
.size
= sizeof(cfg
.arg
);
169 list
= (igs_cfg_sdb_list_t
*)cfg
.arg
;
171 igsc_cfg_request_process(igs_info
->igsc_info
, &cfg
);
172 if (cfg
.status
!= IGSCFG_STATUS_SUCCESS
)
174 IGS_ERROR("Unable to get the IGSDB list\n");
178 bcm_binit(&b
, buf
, size
);
179 bcm_bprintf(&b
, "Group Members Interface\n");
181 for (i
= 0; i
< list
->num_entries
; i
++)
183 bcm_bprintf(&b
, "%08x ", list
->sdb_entry
[i
].mgrp_ip
);
184 bcm_bprintf(&b
, "%08x ", list
->sdb_entry
[i
].mh_ip
);
185 bcm_bprintf(&b
, "%s\n", list
->sdb_entry
[i
].if_name
);
190 IGS_ERROR("Input buffer overflow\n");
194 return (b
.buf
- b
.origbuf
);
196 #endif /* CONFIG_PROC_FS */
199 * Description: This function is called when user application enables snooping
200 * on a bridge interface. It primarily allocates memory for IGS
201 * instance data and calls common code initialization function.
204 igs_instance_add(int8
*inst_id
, struct net_device
*br_ptr
)
206 igs_info_t
*igs_info
;
211 if (igs
.inst_count
> IGS_MAX_INST
)
213 IGS_ERROR("Max instance limit %d exceeded\n", IGS_MAX_INST
);
219 IGS_INFO("Creating IGS instance for %s\n", inst_id
);
221 osh
= osl_attach(NULL
, PCI_BUS
, FALSE
);
225 /* Allocate os specfic IGS info object */
226 igs_info
= MALLOC(osh
, sizeof(igs_info_t
));
227 if (igs_info
== NULL
)
229 IGS_ERROR("Out of memory allocating igs_info\n");
236 /* Save the IGS instance identifier */
237 strncpy(igs_info
->inst_id
, inst_id
, IFNAMSIZ
);
238 igs_info
->inst_id
[IFNAMSIZ
- 1] = 0;
240 /* Save the device pointer */
241 igs_info
->br_dev
= br_ptr
;
243 /* Fill in linux specific wrapper functions*/
244 igsl
.igs_broadcast
= (igs_broadcast_fn_ptr
)igs_broadcast
;
246 /* Initialize IGSC layer */
247 if ((igs_info
->igsc_info
= igsc_init(inst_id
, (void *)igs_info
, osh
, &igsl
)) == NULL
)
249 IGS_ERROR("IGSC init failed\n");
250 MFREE(osh
, igs_info
, sizeof(igs_info_t
));
255 #ifdef CONFIG_PROC_FS
256 sprintf(proc_name
, "net/igs_stats_%s", inst_id
);
257 create_proc_read_entry(proc_name
, 0, 0, igs_stats_get
, igs_info
);
258 sprintf(proc_name
, "net/igsdb_%s", inst_id
);
259 create_proc_read_entry(proc_name
, 0, 0, igs_sdb_list
, igs_info
);
260 #endif /* CONFIG_PROC_FS */
262 IGS_INFO("Created IGSC instance for %s\n", inst_id
);
264 /* Add to global IGS instance list */
266 igs_info
->next
= igs
.list_head
;
267 igs
.list_head
= igs_info
;
268 OSL_UNLOCK(igs
.lock
);
274 igs_instance_del(igs_info_t
*igs_info
)
278 igs_info_t
*ptr
, *prev
;
283 /* Delete the IGS instance */
285 for (ptr
= igs
.list_head
; ptr
!= NULL
; prev
= ptr
, ptr
= ptr
->next
)
291 prev
->next
= ptr
->next
;
293 igs
.list_head
= NULL
;
298 OSL_UNLOCK(igs
.lock
);
302 IGS_ERROR("IGS instance not found\n");
306 /* Free the IGS instance */
307 igsc_exit(igs_info
->igsc_info
);
309 #ifdef CONFIG_PROC_FS
310 sprintf(proc_name
, "net/igs_stats_%s", igs_info
->inst_id
);
311 remove_proc_entry(proc_name
, 0);
312 sprintf(proc_name
, "net/igsdb_%s", igs_info
->inst_id
);
313 remove_proc_entry(proc_name
, 0);
314 #endif /* CONFIG_PROC_FS */
317 MFREE(igs_info
->osh
, igs_info
, sizeof(igs_info_t
));
324 igs_instances_clear(void)
326 igs_info_t
*ptr
, *tmp
;
335 igs_instance_del(ptr
);
339 OSL_UNLOCK(igs
.lock
);
345 igs_instance_find(int8
*inst_id
)
347 igs_info_t
*igs_info
;
349 ASSERT(inst_id
!= NULL
);
353 for (igs_info
= igs
.list_head
; igs_info
!= NULL
; igs_info
= igs_info
->next
)
355 if (strcmp(igs_info
->inst_id
, inst_id
) == 0)
357 OSL_UNLOCK(igs
.lock
);
362 OSL_UNLOCK(igs
.lock
);
368 igs_if_name_validate(uint8
*if_name
)
370 struct net_device
*dev
;
372 /* Get the interface pointer */
373 dev
= dev_get_by_name(if_name
);
377 IGS_ERROR("Interface %s doesn't exist\n", if_name
);
387 * Description: This function handles the OS specific processing
388 * required for configuration commands.
390 * Input: data - Configuration command parameters
393 igs_cfg_request_process(igs_cfg_request_t
*cfg
)
395 igs_info_t
*igs_info
;
396 struct net_device
*br_ptr
;
400 cfg
->status
= IGSCFG_STATUS_FAILURE
;
401 cfg
->size
= sprintf(cfg
->arg
, "Invalid input buffer passed\n");
405 /* Validate the instance identifier */
406 br_ptr
= igs_if_name_validate(cfg
->inst_id
);
409 cfg
->status
= IGSCFG_STATUS_FAILURE
;
410 cfg
->size
= sprintf(cfg
->arg
, "Unknown instance identifier %s\n",
415 /* Locate the IGS instance */
416 igs_info
= igs_instance_find(cfg
->inst_id
);
417 if ((igs_info
== NULL
) && (cfg
->command_id
!= IGSCFG_CMD_BR_ADD
))
419 cfg
->status
= IGSCFG_STATUS_FAILURE
;
420 cfg
->size
= sprintf(cfg
->arg
, "Invalid instance identifier %s\n",
425 /* Convert the interface name in arguments to interface pointer */
426 switch (cfg
->command_id
)
428 case IGSCFG_CMD_BR_ADD
:
429 if (igs_info
!= NULL
)
431 cfg
->status
= IGSCFG_STATUS_FAILURE
;
432 cfg
->size
= sprintf(cfg
->arg
,
433 "IGMP Snooping already enabled on %s\n",
438 /* Create a new IGS instance corresponding to the bridge
441 igs_info
= igs_instance_add(cfg
->inst_id
, br_ptr
);
443 if (igs_info
== NULL
)
445 cfg
->status
= IGSCFG_STATUS_FAILURE
;
446 cfg
->size
= sprintf(cfg
->arg
,
447 "IGMP Snooping enable on %s failed\n",
452 cfg
->status
= IGSCFG_STATUS_SUCCESS
;
455 case IGSCFG_CMD_BR_DEL
:
456 /* Delete and free the IGS instance */
457 if (igs_instance_del(igs_info
) != SUCCESS
)
459 cfg
->status
= IGSCFG_STATUS_FAILURE
;
460 cfg
->size
= sprintf(cfg
->arg
,
461 "IGMP Snooping disable failed\n");
465 cfg
->status
= IGSCFG_STATUS_SUCCESS
;
468 case IGSCFG_CMD_BR_LIST
:
472 igsc_cfg_request_process(igs_info
->igsc_info
, cfg
);
479 * Description: This function is called by Linux kernel when user
480 * applications sends a message on netlink socket. It
481 * dequeues the message, calls the functions to process
482 * the commands and sends the result back to user.
484 * Input: skb - Kernel socket structure
487 igs_netlink_sock_cb(struct sk_buff
*skb
)
489 struct nlmsghdr
*nlh
;
491 nlh
= nlmsg_hdr(skb
);
492 IGS_DEBUG("Length of the command buffer %d\n", nlh
->nlmsg_len
);
494 /* Check the buffer for min size */
495 if (skb
->len
< NLMSG_SPACE(0) || skb
->len
< nlh
->nlmsg_len
||
496 nlh
->nlmsg_len
< NLMSG_LENGTH(sizeof(igs_cfg_request_t
)))
498 IGS_ERROR("Configuration request size not > %d\n",
499 sizeof(igs_cfg_request_t
));
503 skb
= skb_clone(skb
, GFP_KERNEL
);
506 nlh
= nlmsg_hdr(skb
);
508 /* Process the message */
509 igs_cfg_request_process((igs_cfg_request_t
*)NLMSG_DATA(nlh
));
511 /* Send the result to user process */
512 NETLINK_CB(skb
).pid
= nlh
->nlmsg_pid
;
513 NETLINK_CB(skb
).dst_group
= 0;
515 netlink_unicast(igs
.nl_sk
, skb
, nlh
->nlmsg_pid
, MSG_DONTWAIT
);
519 * Description: This function is called during module load time. It
520 * primarily allocates memory for IGS OS specific instance
521 * data and calls the common code initialization function.
524 igs_module_init(void)
526 #define NETLINK_IGSC 18
527 igs
.nl_sk
= netlink_kernel_create(NETLINK_IGSC
, 0, igs_netlink_sock_cb
,
530 if (igs
.nl_sk
== NULL
)
532 IGS_ERROR("Netlink kernel socket create failed\n");
536 igs
.lock
= OSL_LOCK_CREATE("IGS Instance List");
538 if (igs
.lock
== NULL
)
540 IGS_ERROR("IGS instance list lock create failed\n");
548 igs_module_exit(void)
550 sock_release(igs
.nl_sk
->sk_socket
);
551 igs_instances_clear();
552 OSL_LOCK_DESTROY(igs
.lock
);
557 module_init(igs_module_init
);
558 module_exit(igs_module_exit
);
560 EXPORT_SYMBOL(igsc_init
);
561 EXPORT_SYMBOL(igsc_exit
);
562 EXPORT_SYMBOL(igsc_sdb_interface_del
);
563 EXPORT_SYMBOL(igsc_interface_rtport_del
);