diff --git a/include/linux/genetlink.h b/include/linux/genetlink.h new file mode 100644 --- /dev/null +++ b/include/linux/genetlink.h @@ -0,0 +1,51 @@ +#ifndef __LINUX_GENERIC_NETLINK_H +#define __LINUX_GENERIC_NETLINK_H + +#include + +#define GENL_NAMSIZ 16 /* length of family name */ + +#define GENL_MIN_ID NLMSG_MIN_TYPE +#define GENL_MAX_ID 1023 + +struct genlmsghdr { + __u8 cmd; + __u8 version; + __u16 reserved; +}; + +#define GENL_HDRLEN NLMSG_ALIGN(sizeof(struct genlmsghdr)) + +/* + * List of reserved static generic netlink identifiers: + */ +#define GENL_ID_GENERATE 0 +#define GENL_ID_CTRL NLMSG_MIN_TYPE + +/************************************************************************** + * Controller + **************************************************************************/ + +enum { + CTRL_CMD_UNSPEC, + CTRL_CMD_NEWFAMILY, + CTRL_CMD_DELFAMILY, + CTRL_CMD_GETFAMILY, + CTRL_CMD_NEWOPS, + CTRL_CMD_DELOPS, + CTRL_CMD_GETOPS, + __CTRL_CMD_MAX, +}; + +#define CTRL_CMD_MAX (__CTRL_CMD_MAX - 1) + +enum { + CTRL_ATTR_UNSPEC, + CTRL_ATTR_FAMILY_ID, + CTRL_ATTR_FAMILY_NAME, + __CTRL_ATTR_MAX, +}; + +#define CTRL_ATTR_MAX (__CTRL_ATTR_MAX - 1) + +#endif /* __LINUX_GENERIC_NETLINK_H */ diff --git a/include/linux/mashimaro.h b/include/linux/mashimaro.h new file mode 100644 --- /dev/null +++ b/include/linux/mashimaro.h @@ -0,0 +1,28 @@ +#ifndef __LINUX_MASHIMARO_H_ +#define __LINUX_MASHIMARO_H_ + +#include + +#define MM_MAX_LIKES 32 + +enum { + MM_ATTR_UNSPEC, + MM_ATTR_ID, /* u32 */ + MM_ATTR_VALUE, /* string */ + __MM_ATTR_MAX, +}; + +#define MM_ATTR_MAX (__MM_ATTR_MAX - 1) + +enum { + MM_CMD_UNSPEC, + MM_CMD_GETATTR, + MM_CMD_SETATTR, + MM_CMD_NEWATTR, + MM_CMD_DELATTR, + __MM_CMD_MAX, +}; + +#define MM_CMD_MAX (__MM_CMD_MAX - 1) + +#endif diff --git a/include/linux/netlink.h b/include/linux/netlink.h --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -71,7 +71,8 @@ struct nlmsghdr #define NLMSG_ALIGNTO 4 #define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) ) -#define NLMSG_LENGTH(len) ((len)+NLMSG_ALIGN(sizeof(struct nlmsghdr))) +#define NLMSG_HDRLEN ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr))) +#define NLMSG_LENGTH(len) ((len)+NLMSG_ALIGN(NLMSG_HDRLEN)) #define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len)) #define NLMSG_DATA(nlh) ((void*)(((char*)nlh) + NLMSG_LENGTH(0))) #define NLMSG_NEXT(nlh,len) ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \ @@ -86,6 +87,8 @@ struct nlmsghdr #define NLMSG_DONE 0x3 /* End of a dump */ #define NLMSG_OVERRUN 0x4 /* Data lost */ +#define NLMSG_MIN_TYPE 0x10 /* Reserved for control messages */ + struct nlmsgerr { int error; @@ -108,6 +111,30 @@ enum { NETLINK_CONNECTED, }; +/* + * <------- NLA_HDRLEN ------> <-- NLA_ALIGN(payload)--> + * +---------------------+- - -+- - - - - - - - - -+- - -+ + * | Header | Pad | Payload | Pad | + * | (struct nlattr) | ing | | ing | + * +---------------------+- - -+- - - - - - - - - -+- - -+ + * <-------------- nlattr->nla_len --------------> + */ + +struct nlattr +{ + __u16 nla_len; + __u16 nla_type; +}; + +#define NLA_ALIGNTO 4 +#define NLA_ALIGN(len) (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1)) +#define NLA_HDRLEN ((int) NLA_ALIGN(sizeof(struct nlattr))) + +static inline u32 nl_mgrp(u32 group) +{ + return group ? 1 << group : 0; +} + #ifdef __KERNEL__ #include diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h --- a/include/linux/rtnetlink.h +++ b/include/linux/rtnetlink.h @@ -850,8 +850,8 @@ enum /* RTnetlink multicast groups */ enum rtnetlink_groups { - RTNLGRP_NONE, -#define RTNLGRP_NONE RTNLGRP_NONE +// RTNLGRP_NONE, +//#define RTNLGRP_NONE RTNLGRP_NONE RTNLGRP_LINK, #define RTNLGRP_LINK RTNLGRP_LINK RTNLGRP_NOTIFY, diff --git a/include/net/genetlink.h b/include/net/genetlink.h new file mode 100644 --- /dev/null +++ b/include/net/genetlink.h @@ -0,0 +1,154 @@ +#ifndef __NET_GENERIC_NETLINK_H +#define __NET_GENERIC_NETLINK_H + +#include +#include + +/** + * struct genl_family - generic netlink family + * @id: protocol family idenfitier + * @hdrsize: length of user specific header in bytes + * @name: name of family + * @version: protocol version + * @maxattr: maximum number of attributes supported + * @attrbuf: buffer to store parsed attributes + * @ops_list: list of all assigned operations + * @family_list: family list + */ +struct genl_family +{ + unsigned int id; + unsigned int hdrsize; + char name[GENL_NAMSIZ]; + unsigned int version; + unsigned int maxattr; + struct module * owner; + struct nlattr ** attrbuf; /* private */ + struct list_head ops_list; /* private */ + struct list_head family_list; /* private */ +}; + +#define GENL_ADMIN_PERM 0x01 + +/** + * struct genl_info - receiving information + * @snd_seq: sending sequence number + * @snd_pid: netlink pid of sender + * @nlhdr: netlink message header + * @genlhdr: generic netlink message header + * @userhdr: user specific header + * @attrs: netlink attributes + */ +struct genl_info +{ + u32 snd_seq; + u32 snd_pid; + struct nlmsghdr * nlhdr; + struct genlmsghdr * genlhdr; + void * userhdr; + struct nlattr ** attrs; +}; + +/** + * struct genl_ops - generic netlink operations + * @cmd: command identifier + * @flags: flags + * @policy: attribute validation policy + * @doit: standard command callback + * @dumpit: callback for dumpers + * @ops_list: operations list + */ +struct genl_ops +{ + unsigned int cmd; + unsigned int flags; + struct nla_policy *policy; + int (*doit)(struct sk_buff *skb, + struct genl_info *info); + int (*dumpit)(struct sk_buff *skb, + struct netlink_callback *cb); + struct list_head ops_list; +}; + +extern int genl_register_family(struct genl_family *family); +extern int genl_unregister_family(struct genl_family *family); +extern int genl_register_ops(struct genl_family *, struct genl_ops *ops); +extern int genl_unregister_ops(struct genl_family *, struct genl_ops *ops); + +extern struct sock *genl_sock; + +/** + * genlmsg_put - Add generic netlink header to netlink message + * @skb: socket buffer holding the message + * @pid: netlink pid the message is addressed to + * @seq: sequence number (usually the one of the sender) + * @type: netlink message type + * @hdrlen: length of the user specific header + * @flags netlink message flags + * @cmd: generic netlink command + * @version: version + * + * Returns pointer to user specific header + */ +static inline void *genlmsg_put(struct sk_buff *skb, u32 pid, u32 seq, + int type, int hdrlen, int flags, + u8 cmd, u8 version) +{ + struct nlmsghdr *nlh; + struct genlmsghdr *hdr; + + nlh = nlmsg_put(skb, pid, seq, type, GENL_HDRLEN + hdrlen, flags); + if (nlh == NULL) + return NULL; + + hdr = nlmsg_data(nlh); + hdr->cmd = cmd; + hdr->version = version; + hdr->reserved = 0; + + return (char *) hdr + GENL_HDRLEN; +} + +/** + * genlmsg_end - Finalize a generic netlink message + * @skb: socket buffer the message is stored in + * @hdr: user specific header + */ +static inline int genlmsg_end(struct sk_buff *skb, void *hdr) +{ + return nlmsg_end(skb, hdr - GENL_HDRLEN - NLMSG_HDRLEN); +} + +/** + * genlmsg_cancel - Cancel construction of a generic netlink message + * @skb: socket buffer the message is stored in + * @hdr: generic netlink message header + */ +static inline int genlmsg_cancel(struct sk_buff *skb, void *hdr) +{ + return nlmsg_cancel(skb, hdr - GENL_HDRLEN - NLMSG_HDRLEN); +} + +/** + * genlmsg_multicast - multicast a netlink message + * @skb: netlink message as socket buffer + * @pid: own netlink pid to avoid sending to yourself + * @group: multicast group id + */ +static inline int genlmsg_multicast(struct sk_buff *skb, u32 pid, + unsigned int group) +{ + return nlmsg_multicast(genl_sock, skb, pid, group); +} + +/** + * genlmsg_unicast - unicast a netlink message + * @skb: netlink message as socket buffer + * @pid: netlink pid of the destination socket + */ +static inline int genlmsg_unicast(struct sk_buff *skb, u32 pid) +{ + return nlmsg_unicast(genl_sock, skb, pid); +} + +#endif /* __NET_GENERIC_NETLINK_H */ diff --git a/include/net/netlink.h b/include/net/netlink.h new file mode 100644 --- /dev/null +++ b/include/net/netlink.h @@ -0,0 +1,947 @@ +#ifndef __NET_NETLINK_H +#define __NET_NETLINK_H + +#include +#include + +/* ======================================================================== + * Netlink Messages and Attributes Interface (As Seen On TV) + * ------------------------------------------------------------------------ + * Messages Interface + * ------------------------------------------------------------------------ + * + * Message Format: + * <--- nlmsg_total_size(payload) ---> + * <-- nlmsg_msg_size(payload) -> + * +----------+- - -+-------------+- - -+-------- - - + * | nlmsghdr | Pad | Payload | Pad | nlmsghdr + * +----------+- - -+-------------+- - -+-------- - - + * nlmsg_data(nlh)---^ ^ + * nlmsg_next(nlh)-----------------------+ + * + * Payload Format: + * <---------------------- nlmsg_len(nlh) ---------------------> + * <------ hdrlen ------> <- nlmsg_attrlen(nlh, hdrlen) -> + * +----------------------+- - -+--------------------------------+ + * | Family Header | Pad | Attributes | + * +----------------------+- - -+--------------------------------+ + * nlmsg_attrdata(nlh, hdrlen)---^ + * + * Data Structures: + * struct nlmsghdr netlink message header + * + * Message Construction: + * nlmsg_new() create a new netlink message + * nlmsg_put() add a netlink message to an skb + * nlmsg_put_answer() callback based nlmsg_put() + * nlmsg_end() finanlize netlink message + * nlmsg_cancel() cancel message construction + * nlmsg_free() free a netlink message + * + * Message Sending: + * nlmsg_multicast() multicast message to several groups + * nlmsg_unicast() unicast a message to a single socket + * + * Message Length Calculations: + * nlmsg_msg_size(payload) length of message w/o padding + * nlmsg_total_size(payload) length of message w/ padding + * nlmsg_padlen(payload) length of padding at tail + * + * Message Payload Access: + * nlmsg_data(nlh) head of message payload + * nlmsg_len(nlh) length of message payload + * nlmsg_attrdata(nlh, hdrlen) head of attributes data + * nlmsg_attrlen(nlh, hdrlen) length of attributes data + * + * Message Parsing: + * nlmsg_ok(nlh, remaining) does nlh fit into remaining bytes? + * nlmsg_next(nlh, remaining) get next netlink message + * nlmsg_parse() parse attributes of a message + * nlmsg_find_attr() find an attribute in a message + * nlmsg_for_each_msg() loop over all messages + * nlmsg_validate() validate netlink message incl. attrs + * nlmsg_for_each_attr() loop over all attributes + * + * ------------------------------------------------------------------------ + * Attributes Interface + * ------------------------------------------------------------------------ + * + * Attribute Format: + * <------- nla_total_size(payload) -------> + * <---- nla_attr_size(payload) -----> + * +----------+- - -+- - - - - - - - - +- - -+-------- - - + * | Header | Pad | Payload | Pad | Header + * +----------+- - -+- - - - - - - - - +- - -+-------- - - + * <- nla_len(nla) -> ^ + * nla_data(nla)----^ | + * nla_next(nla)-----------------------------' + * + * Data Structures: + * struct nlattr netlink attribtue header + * + * Attribute Construction: + * nla_reserve(skb, type, len) reserve skb tailroom for an attribute + * nla_put(skb, type, len, data) add attribute to skb + * + * Attribute Construction for Basic Types: + * nla_put_u8(skb, type, value) add u8 attribute to skb + * nla_put_u16(skb, type, value) add u16 attribute to skb + * nla_put_u32(skb, type, value) add u32 attribute to skb + * nla_put_u64(skb, type, value) add u64 attribute to skb + * nla_put_string(skb, type, str) add string attribute to skb + * nla_put_flag(skb, type) add flag attribute to skb + * nla_put_msecs(skb, type, jiffies) add msecs attribute to skb + * + * Exceptions Based Attribute Construction: + * NLA_PUT(skb, type, len, data) add attribute to skb + * NLA_PUT_U8(skb, type, value) add u8 attribute to skb + * NLA_PUT_U16(skb, type, value) add u16 attribute to skb + * NLA_PUT_U32(skb, type, value) add u32 attribute to skb + * NLA_PUT_U64(skb, type, value) add u64 attribute to skb + * NLA_PUT_STRING(skb, type, str) add string attribute to skb + * NLA_PUT_FLAG(skb, type) add flag attribute to skb + * NLA_PUT_MSECS(skb, type, jiffies) add msecs attribute to skb + * + * The meaning of these functions is equal to their lower case + * variants but they jump to the label nla_put_failure in case + * of a failure. + * + * Nested Attributes Construction: + * nla_nest_start(skb, type) start a nested attribute + * nla_nest_end(skb, nla) finalize a nested attribute + * nla_nest_cancel(skb, nla) cancel nested attribute construction + * + * Attribute Length Calculations: + * nla_attr_size(payload) length of attribute w/o padding + * nla_total_size(payload) length of attribute w/ padding + * nla_padlen(payload) length of padding + * + * Attribute Payload Access: + * nla_data(nla) head of attribute payload + * nla_len(nla) length of attribute payload + * + * Attribute Payload Access for Basic Types: + * nla_get_u8(nla) get payload for a u8 attribute + * nla_get_u16(nla) get payload for a u16 attribute + * nla_get_u32(nla) get payload for a u32 attribute + * nla_get_u64(nla) get payload for a u64 attribute + * nla_get_flag(nla) return 1 if flag is true + * nla_get_msecs(nla) get payload for a msecs attribute + * + * Attribute Misc: + * nla_memcpy(dest, nla, count) copy attribute into memory + * nla_memcmp(nla, data, size) compare attribute with memory area + * nla_strlcpy(dst, nla, size) copy attribute to a sized string + * nla_strcmp(nla, str) compare attribute with string + * + * Attribute Parsing: + * nla_ok(nla, remaining) does nla fit into remaining bytes? + * nla_next(nla, remaining) get next netlink attribute + * nla_validate() validate a stream of attributes + * nla_find() find attribute in stream of attributes + * nla_parse() parse and validate stream of attrs + * nla_parse_nested() parse nested attribuets + * nla_for_each_attr() loop over all attributes + *========================================================================= + */ + + /** + * Standard attribute types to specify validation policy + */ +enum { + NLA_UNSPEC, + NLA_U8, + NLA_U16, + NLA_U32, + NLA_U64, + NLA_STRING, + NLA_FLAG, + NLA_MSECS, + NLA_NESTED, + __NLA_TYPE_MAX, +}; + +#define NLA_TYPE_MAX (__NLA_TYPE_MAX - 1) + +/** + * struct nla_policy - attribute validation policy + * @type: Type of attribute or NLA_UNSPEC + * @minlen: Minimal length of payload required to be available + * + * Policies are defined as arrays of this struct, the array must + * be accessible by attribute type up to the highest identifier + * to be expected. + * + * Example: + * static struct nla_policy my_policy[ATTR_MAX+1] __read_mostly = { + * [ATTR_FOO] = { .type = NLA_U16 }, + * [ATTR_BAR] = { .type = NLA_STRING }, + * [ATTR_BAZ] = { .minlen = sizeof(struct mystruct) }, + * }; + */ +struct nla_policy { + u16 type; + u16 minlen; +}; + +extern int netlink_dequeue(struct sock *sk, + int (*cb)(struct sk_buff *, + struct nlmsghdr *, int *)); + +extern int nla_validate(struct nlattr *head, int len, int maxtype, + struct nla_policy *policy); +extern int nla_parse(struct nlattr *tb[], int maxtype, + struct nlattr *head, int len, + struct nla_policy *policy); +extern struct nlattr * nla_find(struct nlattr *head, int len, int attrtype); +extern size_t nla_strlcpy(char *dst, const struct nlattr *nla, + size_t dstsize); +extern int nla_memcpy(void *dest, struct nlattr *src, int count); +extern int nla_memcmp(const struct nlattr *nla, const void *data, + size_t size); +extern int nla_strcmp(const struct nlattr *nla, const char *str); +extern void __nla_put(struct sk_buff *skb, int attrtype, + int attrlen, const void *data); + + +/************************************************************************** + * Netlink Messages + **************************************************************************/ + +/** + * nlmsg_msg_size - length of netlink message not including padding + * @payload: length of message payload + */ +static inline int nlmsg_msg_size(int payload) +{ + return NLMSG_HDRLEN + payload; +} + +/** + * nlmsg_total_size - length of netlink message including padding + * @payload: length of message payload + */ +static inline int nlmsg_total_size(int payload) +{ + return NLMSG_ALIGN(nlmsg_msg_size(payload)); +} + +/** + * nlmsg_padlen - length of padding at the message's tail + * @payload: length of message payload + */ +static inline int nlmsg_padlen(int payload) +{ + return nlmsg_total_size(payload) - nlmsg_msg_size(payload); +} + +/** + * nlmsg_data - head of message payload + * @nlh: netlink messsage header + */ +static inline void *nlmsg_data(const struct nlmsghdr *nlh) +{ + return (unsigned char *) nlh + NLMSG_HDRLEN; +} + +/** + * nlmsg_len - length of message payload + * @nlh: netlink message header + */ +static inline int nlmsg_len(const struct nlmsghdr *nlh) +{ + return nlh->nlmsg_len - NLMSG_HDRLEN; +} + +/** + * nlmsg_attrdata - head of attributes data + * @nlh: netlink message header + * @hdrlen: length of family specific header + */ +static inline struct nlattr *nlmsg_attrdata(const struct nlmsghdr *nlh, + int hdrlen) +{ + unsigned char *data = nlmsg_data(nlh); + return (struct nlattr *) (data + NLMSG_ALIGN(hdrlen)); +} + +/** + * nlmsg_attrlen - length of attributes data + * @nlh: netlink message header + * @hdrlen: length of family specific header + */ +static inline int nlmsg_attrlen(const struct nlmsghdr *nlh, int hdrlen) +{ + return nlmsg_len(nlh) - NLMSG_ALIGN(hdrlen); +} + +/** + * nlmsg_ok - check if the netlink message fits into the remaining bytes + * @nlh: netlink message header + * @remaining: number of bytes remaining in message stream + */ +static inline int nlmsg_ok(const struct nlmsghdr *nlh, int remaining) +{ + return (remaining >= sizeof(struct nlmsghdr) && + nlh->nlmsg_len >= sizeof(struct nlmsghdr) && + nlh->nlmsg_len <= remaining); +} + +/** + * nlmsg_next - next netlink message in message stream + * @nlh: netlink message header + * @remaining: number of bytes remaining in message stream + * + * Returns the next netlink message in the message stream and + * decrements remaining by the size of the current message. + */ +static inline struct nlmsghdr *nlmsg_next(struct nlmsghdr *nlh, int *remaining) +{ + int totlen = NLMSG_ALIGN(nlh->nlmsg_len); + + *remaining -= totlen; + + return (struct nlmsghdr *) ((unsigned char *) nlh + totlen); +} + +/** + * nlmsg_parse - parse attributes of a netlink message + * @nlh: netlink message header + * @hdrlen: length of family specific header + * @tb: destination array with maxtype+1 elements + * @maxtype: maximum attribute type to be expected + * @policy: validation policy + * + * See nla_parse() + */ +static inline int nlmsg_parse(struct nlmsghdr *nlh, int hdrlen, + struct nlattr *tb[], int maxtype, + struct nla_policy *policy) +{ + if (nlh->nlmsg_len < nlmsg_msg_size(hdrlen)) + return -EINVAL; + + return nla_parse(tb, maxtype, nlmsg_attrdata(nlh, hdrlen), + nlmsg_attrlen(nlh, hdrlen), policy); +} + +/** + * nlmsg_find_attr - find a specific attribute in a netlink message + * @nlh: netlink message header + * @hdrlen: length of familiy specific header + * @attrtype: type of attribute to look for + * + * Returns the first attribute which matches the specified type. + */ +static inline struct nlattr *nlmsg_find_attr(struct nlmsghdr *nlh, + int hdrlen, int attrtype) +{ + return nla_find(nlmsg_attrdata(nlh, hdrlen), + nlmsg_attrlen(nlh, hdrlen), attrtype); +} + +/** + * nlmsg_validate - validate a netlink message including attributes + * @nlh: netlinket message header + * @hdrlen: length of familiy specific header + * @maxtype: maximum attribute type to be expected + * @policy: validation policy + */ +static inline int nlmsg_validate(struct nlmsghdr *nlh, int hdrlen, int maxtype, + struct nla_policy *policy) +{ + if (nlh->nlmsg_len < nlmsg_msg_size(hdrlen)) + return -EINVAL; + + return nla_validate(nlmsg_attrdata(nlh, hdrlen), + nlmsg_attrlen(nlh, hdrlen), maxtype, policy); +} + +/** + * nlmsg_for_each_attr - iterate over a stream of attributes + * @pos: loop counter, set to current attribute + * @nlh: netlink message header + * @hdrlen: length of familiy specific header + * @rem: initialized to len, holds bytes currently remaining in stream + */ +#define nlmsg_for_each_attr(pos, nlh, hdrlen, rem) \ + nla_for_each_attr(pos, nlmsg_attrdata(nlh, hdrlen), \ + nlmsg_attrlen(nlh, hdrlen), rem) + +#if 0 +/* + * FIXME: Uncomment once all users have been converted + */ + +/** + * __nlmsg_put - Add a new netlink message to an skb + * @skb: socket buffer to store message in + * @pid: netlink process id + * @seq: sequence number of message + * @type: message type + * @payload: length of message payload + * @flags: message flags + * + * The caller is responsible to ensure that the skb provides enough + * tailroom for both the netlink header and payload. + */ +static inline struct nlmsghdr *__nlmsg_put(struct sk_buff *skb, u32 pid, + u32 seq, int type, int payload, + int flags) +{ + struct nlmsghdr *nlh; + + nlh = (struct nlmsghdr *) skb_put(skb, nlmsg_total_size(payload)); + nlh->nlmsg_type = type; + nlh->nlmsg_len = nlmsg_msg_size(payload); + nlh->nlmsg_flags = flags; + nlh->nlmsg_pid = pid; + nlh->nlmsg_seq = seq; + + memset((unsigned char *) nlmsg_data(nlh) + payload, 0, + nlmsg_padlen(payload)); + + return nlh; +} +#endif + +/** + * nlmsg_put - Add a new netlink message to an skb + * @skb: socket buffer to store message in + * @pid: netlink process id + * @seq: sequence number of message + * @type: message type + * @payload: length of message payload + * @flags: message flags + * + * Returns NULL if the tailroom of the skb is insufficient to store + * the message header and payload. + */ +static inline struct nlmsghdr *nlmsg_put(struct sk_buff *skb, u32 pid, u32 seq, + int type, int payload, int flags) +{ + if (unlikely(skb_tailroom(skb) < nlmsg_total_size(payload))) + return NULL; + + return __nlmsg_put(skb, pid, seq, type, payload, flags); +} + +/** + * nlmsg_put_answer - Add a new callback based netlink message to an skb + * @skb: socket buffer to store message in + * @cb: netlink callback + * @type: message type + * @payload: length of message payload + * @flags: message flags + * + * Returns NULL if the tailroom of the skb is insufficient to store + * the message header and payload. + */ +static inline struct nlmsghdr *nlmsg_put_answer(struct sk_buff *skb, + struct netlink_callback *cb, + int type, int payload, + int flags) +{ + return nlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, + type, payload, flags); +} + +/** + * nlmsg_new - Allocate a new netlink message + * @size: maximum size of message + * + * Use NLMSG_GOODSIZE if size isn't know and you need a good default size. + */ +static inline struct sk_buff *nlmsg_new(int size) +{ + return alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); +} + +/** + * nlmsg_end - Finalize a netlink message + * @skb: socket buffer the message is stored in + * @nlh: netlink message header + * + * Corrects the netlink message header to include the appeneded + * attributes. Only necessary if attributes have been added to + * the message. + * + * Returns the total data length of the skb. + */ +static inline int nlmsg_end(struct sk_buff *skb, struct nlmsghdr *nlh) +{ + nlh->nlmsg_len = skb->tail - (unsigned char *) nlh; + + return skb->len; +} + +/** + * nlmsg_cancel - Cancel construction of a netlink message + * @skb: socket buffer the message is stored in + * @nlh: netlink message header + * + * Removes the complete netlink message including all + * attributes from the socket buffer again. Returns -1. + */ +static inline int nlmsg_cancel(struct sk_buff *skb, struct nlmsghdr *nlh) +{ + skb_trim(skb, (unsigned char *) nlh - skb->data); + + return -1; +} + +/** + * nlmsg_free - free a netlink message + * @skb: socket buffer of netlink message + */ +static inline void nlmsg_free(struct sk_buff *skb) +{ + kfree_skb(skb); +} + +/** + * nlmsg_multicast - multicast a netlink message + * @sk: netlink socket to spread messages to + * @skb: netlink message as socket buffer + * @pid: own netlink pid to avoid sending to yourself + * @group: multicast group id + */ +static inline int nlmsg_multicast(struct sock *sk, struct sk_buff *skb, + u32 pid, unsigned int group) +{ + int err; + + NETLINK_CB(skb).dst_group = group; + + err = netlink_broadcast(sk, skb, pid, group, GFP_KERNEL); + if (err > 0) + err = 0; + + return err; +} + +/** + * nlmsg_unicast - unicast a netlink message + * @sk: netlink socket to spread message to + * @skb: netlink message as socket buffer + * @pid: netlink pid of the destination socket + */ +static inline int nlmsg_unicast(struct sock *sk, struct sk_buff *skb, u32 pid) +{ + int err; + + err = netlink_unicast(sk, skb, pid, MSG_DONTWAIT); + if (err > 0) + err = 0; + + return err; +} + +/** + * nlmsg_for_each_msg - iterate over a stream of messages + * @pos: loop counter, set to current message + * @head: head of message stream + * @len: length of message stream + * @rem: initialized to len, holds bytes currently remaining in stream + */ +#define nlmsg_for_each_msg(pos, head, len, rem) \ + for (pos = head, rem = len; \ + nlmsg_ok(pos, rem); \ + pos = nlmsg_next(pos, &(rem))) + +/************************************************************************** + * Netlink Attributes + **************************************************************************/ + +/** + * nla_attr_size - length of attribute not including padding + * @payload: length of payload + */ +static inline int nla_attr_size(int payload) +{ + return NLA_HDRLEN + payload; +} + +/** + * nla_total_size - total length of attribute including padding + * @payload: length of payload + */ +static inline int nla_total_size(int payload) +{ + return NLA_ALIGN(nla_attr_size(payload)); +} + +/** + * nla_padlen - length of padding at the tail of attribute + * @payload: length of payload + */ +static inline int nla_padlen(int payload) +{ + return nla_total_size(payload) - nla_attr_size(payload); +} + +/** + * nla_data - head of payload + * @nla: netlink attribute + */ +static inline void *nla_data(const struct nlattr *nla) +{ + return (char *) nla + NLA_HDRLEN; +} + +/** + * nla_len - length of payload + * @nla: netlink attribute + */ +static inline int nla_len(const struct nlattr *nla) +{ + return nla->nla_len - NLA_HDRLEN; +} + +/** + * nla_ok - check if the netlink attribute fits into the remaining bytes + * @nla: netlink attribute + * @remaining: number of bytes remaining in attribute stream + */ +static inline int nla_ok(const struct nlattr *nla, int remaining) +{ + return remaining >= sizeof(*nla) && + nla->nla_len >= sizeof(*nla) && + nla->nla_len <= remaining; +} + +/** + * nla_next - next netlink attribte in attribute stream + * @nla: netlink attribute + * @remaining: number of bytes remaining in attribute stream + * + * Returns the next netlink attribute in the attribute stream and + * decrements remaining by the size of the current attribute. + */ +static inline struct nlattr *nla_next(const struct nlattr *nla, int *remaining) +{ + int totlen = NLA_ALIGN(nla->nla_len); + + *remaining -= totlen; + return (struct nlattr *) ((char *) nla + totlen); +} + +/** + * nla_parse_nested - parse nested attributes + * @tb: destination array with maxtype+1 elements + * @maxtype: maximum attribute type to be expected + * @nla: attribute containing the nested attributes + * @policy: validation policy + * + * See nla_parse() + */ +static inline int nla_parse_nested(struct nlattr *tb[], int maxtype, + struct nlattr *nla, + struct nla_policy *policy) +{ + return nla_parse(tb, maxtype, nla_data(nla), nla_len(nla), policy); +} + +/** + * __nla_reserve - reserve room for attribute on the skb + * @skb: socket buffer to reserve room on + * @attrtype: attribute type + * @attrlen: length of attribute payload + * + * Adds a netlink attribute header to a socket buffer and reserves + * room for the payload but does not copy it. + * + * The caller is responsible to ensure that the skb provides enough + * tailroom for the attribute header and payload. + */ +static inline struct nlattr *__nla_reserve(struct sk_buff *skb, int attrtype, + int attrlen) +{ + struct nlattr *nla; + + nla = (struct nlattr *) skb_put(skb, nla_total_size(attrlen)); + nla->nla_type = attrtype; + nla->nla_len = nla_attr_size(attrlen); + + memset((unsigned char *) nla + nla->nla_len, 0, nla_padlen(attrlen)); + + return nla; +} + +/** + * nla_reserve - reserve room for attribute on the skb + * @skb: socket buffer to reserve room on + * @attrtype: attribute type + * @attrlen: length of attribute payload + * + * Adds a netlink attribute header to a socket buffer and reserves + * room for the payload but does not copy it. + * + * Returns NULL if the tailroom of the skb is insufficient to store + * the attribute header and payload. + */ +static inline struct nlattr *nla_reserve(struct sk_buff *skb, int attrtype, + int attrlen) +{ + if (unlikely(skb_tailroom(skb) < nla_total_size(attrlen))) + return NULL; + + return __nla_reserve(skb, attrtype, attrlen); +} + +/** + * nla_put - Add a netlink attribute to a socket buffer + * @skb: socket buffer to add attribute to + * @attrtype: attribute type + * @attrlen: length of attribute payload + * @data: head of attribute payload + * + * Returns -1 if the tailroom of the skb is insufficient to store + * the attribute header and payload. + */ +static inline int nla_put(struct sk_buff *skb, int attrtype, int attrlen, + const void *data) +{ + if (unlikely(skb_tailroom(skb) < nla_total_size(attrlen))) + return -1; + + __nla_put(skb, attrtype, attrlen, data); + return 0; +} + +/** + * nla_put_u8 - Add a u16 netlink attribute to a socket buffer + * @skb: socket buffer to add attribute to + * @attrtype: attribute type + * @value: numeric value + */ +static inline int nla_put_u8(struct sk_buff *skb, int attrtype, u8 value) +{ + return nla_put(skb, attrtype, sizeof(u8), &value); +} + +/** + * nla_put_u16 - Add a u16 netlink attribute to a socket buffer + * @skb: socket buffer to add attribute to + * @attrtype: attribute type + * @value: numeric value + */ +static inline int nla_put_u16(struct sk_buff *skb, int attrtype, u16 value) +{ + return nla_put(skb, attrtype, sizeof(u16), &value); +} + +/** + * nla_put_u32 - Add a u32 netlink attribute to a socket buffer + * @skb: socket buffer to add attribute to + * @attrtype: attribute type + * @value: numeric value + */ +static inline int nla_put_u32(struct sk_buff *skb, int attrtype, u32 value) +{ + return nla_put(skb, attrtype, sizeof(u32), &value); +} + +/** + * nla_put_64 - Add a u64 netlink attribute to a socket buffer + * @skb: socket buffer to add attribute to + * @attrtype: attribute type + * @value: numeric value + */ +static inline int nla_put_u64(struct sk_buff *skb, int attrtype, u64 value) +{ + return nla_put(skb, attrtype, sizeof(u64), &value); +} + +/** + * nla_put_string - Add a string netlink attribute to a socket buffer + * @skb: socket buffer to add attribute to + * @attrtype: attribute type + * @str: NUL terminated string + */ +static inline int nla_put_string(struct sk_buff *skb, int attrtype, + const char *str) +{ + return nla_put(skb, attrtype, strlen(str) + 1, str); +} + +/** + * nla_put_flag - Add a flag netlink attribute to a socket buffer + * @skb: socket buffer to add attribute to + * @attrtype: attribute type + */ +static inline int nla_put_flag(struct sk_buff *skb, int attrtype) +{ + return nla_put(skb, attrtype, 0, NULL); +} + +/** + * nla_put_msecs - Add a msecs netlink attribute to a socket buffer + * @skb: socket buffer to add attribute to + * @attrtype: attribute type + * @jiffies: number of msecs in jiffies + */ +static inline int nla_put_msecs(struct sk_buff *skb, int attrtype, + unsigned long jiffies) +{ + u64 tmp = jiffies_to_msecs(jiffies); + return nla_put(skb, attrtype, sizeof(u64), &tmp); +} + +#define NLA_PUT(skb, attrtype, attrlen, data) \ + do { \ + if (nla_put(skb, attrtype, attrlen, data) < 0) \ + goto nla_put_failure; \ + } while(0) + +#define NLA_PUT_TYPE(skb, type, attrtype, value) \ + do { \ + type __tmp = value; \ + NLA_PUT(skb, attrtype, sizeof(type), &__tmp); \ + } while(0) + +#define NLA_PUT_U8(skb, attrtype, value) \ + NLA_PUT_TYPE(skb, u8, attrtype, value) + +#define NLA_PUT_U16(skb, attrtype, value) \ + NLA_PUT_TYPE(skb, u16, attrtype, value) + +#define NLA_PUT_U32(skb, attrtype, value) \ + NLA_PUT_TYPE(skb, u32, attrtype, value) + +#define NLA_PUT_U64(skb, attrtype, value) \ + NLA_PUT_TYPE(skb, u64, attrtype, value) + +#define NLA_PUT_STRING(skb, attrtype, value) \ + NLA_PUT(skb, attrtype, strlen(value) + 1, value) + +#define NLA_PUT_FLAG(skb, attrtype, value) \ + NLA_PUT(skb, attrtype, 0, NULL) + +#define NLA_PUT_MSECS(skb, attrtype, jiffies) \ + NLA_PUT_U64(skb, attrtype, jiffies_to_msecs(jiffies)) + +/** + * nla_get_u32 - return payload of u32 attribute + * @nla: u32 netlink attribute + */ +static inline u32 nla_get_u32(struct nlattr *nla) +{ + return *(u32 *) nla_data(nla); +} + +/** + * nla_get_u16 - return payload of u16 attribute + * @nla: u16 netlink attribute + */ +static inline u16 nla_get_u16(struct nlattr *nla) +{ + return *(u16 *) nla_data(nla); +} + +/** + * nla_get_u8 - return payload of u8 attribute + * @nla: u8 netlink attribute + */ +static inline u8 nla_get_u8(struct nlattr *nla) +{ + return *(u8 *) nla_data(nla); +} + +/** + * nla_get_u64 - return payload of u64 attribute + * @nla: u64 netlink attribute + */ +static inline u64 nla_get_u64(struct nlattr *nla) +{ + u64 tmp; + + nla_memcpy(&tmp, nla, sizeof(tmp)); + + return tmp; +} + +/** + * nla_get_flag - return payload of flag attribute + * @nla: flag netlink attribute + */ +static inline int nla_get_flag(struct nlattr *nla) +{ + return !!nla; +} + +/** + * nla_get_msecs - return payload of msecs attribute + * @nla: msecs netlink attribute + * + * Returns the number of milliseconds in jiffies. + */ +static inline unsigned long nla_get_msecs(struct nlattr *nla) +{ + u64 msecs = nla_get_u64(nla); + + return msecs_to_jiffies((unsigned long) msecs); +} + +/** + * nla_nest_start - Start a new level of nested attributes + * @skb: socket buffer to add attributes to + * @attrtype: attribute type of container + * + * Returns the container attribute + */ +static inline struct nlattr *nla_nest_start(struct sk_buff *skb, int attrtype) +{ + struct nlattr *start = (struct nlattr *) skb->tail; + + if (nla_put(skb, attrtype, 0, NULL) < 0) + return NULL; + + return start; +} + +/** + * nla_nest_end - Finalize nesting of attributes + * @skb: socket buffer the attribtues are stored in + * @start: container attribute + * + * Corrects the container attribute header to include the all + * appeneded attributes. + * + * Returns the total data length of the skb. + */ +static inline int nla_nest_end(struct sk_buff *skb, struct nlattr *start) +{ + start->nla_len = skb->tail - (unsigned char *) start; + return skb->len; +} + +/** + * nla_nest_cancel - Cancel nesting of attributes + * @skb: socket buffer the message is stored in + * @start: container attribute + * + * Removes the container attribute and including all nested + * attributes. Returns -1. + */ +static inline int nla_nest_cancel(struct sk_buff *skb, struct nlattr *start) +{ + if (start) + skb_trim(skb, (unsigned char *) start - skb->data); + + return -1; +} + +/** + * nla_for_each_attr - iterate over a stream of attributes + * @pos: loop counter, set to current attribute + * @head: head of attribute stream + * @len: length of attribute stream + * @rem: initialized to len, holds bytes currently remaining in stream + */ +#define nla_for_each_attr(pos, head, len, rem) \ + for (pos = head, rem = len; \ + nla_ok(pos, rem); \ + pos = nla_next(pos, &(rem))) + +#endif diff --git a/net/Kconfig b/net/Kconfig --- a/net/Kconfig +++ b/net/Kconfig @@ -209,6 +209,15 @@ config NET_PKTGEN endmenu +menu "Generic Netlink" + +config NET_MASHIMARO + tristate "Mr. Mashimaro" + +config NET_FOOTEST + tristate "Generic Netlink Test" + +endmenu endmenu source "net/ax25/Kconfig" diff --git a/net/core/Makefile b/net/core/Makefile --- a/net/core/Makefile +++ b/net/core/Makefile @@ -16,3 +16,6 @@ obj-$(CONFIG_NET_DIVERT) += dv.o obj-$(CONFIG_NET_PKTGEN) += pktgen.o obj-$(CONFIG_NET_RADIO) += wireless.o obj-$(CONFIG_NETPOLL) += netpoll.o + +obj-$(CONFIG_NET_MASHIMARO) += mashimaro.o +obj-$(CONFIG_NET_FOOTEST) += footest.o diff --git a/net/core/footest.c b/net/core/footest.c new file mode 100644 --- /dev/null +++ b/net/core/footest.c @@ -0,0 +1,215 @@ +/* + * Copyright (C) Jamal Hadi Salim - 2005 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define FOOTEST_GET 0x10 +#define FOOTEST_ADD 0x20 + +struct genl_family foo1 = { + .id = 21, + .name = "foo1", + .maxattr = 2, + .hdrsize = 4, + .version = 0x1, +}; + +struct genl_family foo2 = { + .id = 22, + .name = "foo2", + .hdrsize = 4, + .maxattr = 2, + .version = 0x1, +}; + +struct genl_family foo3 = { + .id = 23, + .name = "foo3", + .hdrsize = 4, + .maxattr = 2, +}; + +struct genl_family foo4 = { + .id = 24, + .name = "foo4", + .maxattr = 2, + .hdrsize = 4, + .version = 0x1, +}; + +struct genl_family foo5 = { + .id = GENL_ID_GENERATE, + .name = "foo5", + .maxattr = 2, + .hdrsize = 4, + .version = 0x1, +}; + +struct genl_family foo6 = { + .id = 24, + .name = "foo6", + .maxattr = 2, + .hdrsize = 4, + .version = 0x1, +}; + +struct genl_ops cmd1 = { + .cmd = FOOTEST_GET, +}; + +struct genl_ops cmd2 = { + .cmd = FOOTEST_ADD, +}; + +struct genl_ops cmd6 = { + .cmd = FOOTEST_GET, +}; + +struct genl_ops cmd7 = { + .cmd = FOOTEST_ADD, + .flags = GENL_ADMIN_PERM, +}; + +int footest_doit(struct sk_buff *skb, struct nlmsghdr *nl) +{ + return 0; +} + +int footest_dumpit(struct sk_buff *skb, struct netlink_callback *cb) +{ + return 0; +} + +static int __init foonltest_init(void) +{ + printk("-----> genl test started ...\n"); +/* test adding and deleting .. */ + printk("test1: insert foo1\n"); + genl_register_family(&foo1); + printk("test2: insert foo2\n"); + genl_register_family(&foo2); + printk("test3: remove foo1\n"); + genl_unregister_family(&foo1); + printk("test4: remove foo2\n"); + genl_unregister_family(&foo2); +/* test adding again with collisions on hash .. */ + printk("test5: insert foo1\n"); + genl_register_family(&foo1); + printk("test6: insert foo2 \n"); + genl_register_family(&foo2); + printk("test7: insert foo3\n"); + genl_register_family(&foo3); + printk("test8: insert foo4 \n"); + genl_register_family(&foo4); + printk("test9: remove foo1 and foo4\n"); + genl_unregister_family(&foo1); + genl_unregister_family(&foo4); + printk("test10: remove foo2 and foo3\n"); + genl_unregister_family(&foo2); + genl_unregister_family(&foo3); + /* Try to delete something that doesnt exist */ + printk("test11: remove non-existent foo2 and foo3\n"); + genl_unregister_family(&foo2); + genl_unregister_family(&foo3); + /* + * Now testing on commands + */ + cmd1.dumpit = (void *)footest_dumpit; + cmd2.doit = (void *)footest_doit; + printk("test12: insert foo1 with two commands\n"); + genl_register_family(&foo1); + genl_register_ops(&foo1, &cmd1); + genl_register_ops(&foo1, &cmd2); + printk("test13: foo1 with the two commands\n"); + genl_unregister_family(&foo1); + /* */ + printk("test14: insert foo3 and foo4 with commands\n"); + genl_register_family(&foo3); + genl_register_family(&foo4); + genl_register_ops(&foo4, &cmd1); + genl_register_ops(&foo3, &cmd2); + printk("test15: unregister foo4 and foo3\n"); + genl_unregister_family(&foo4); + genl_unregister_family(&foo3); + /* */ + printk("test16: insert foo5 - dynamic ID with 2 commands\n"); + genl_register_family(&foo5); + genl_register_ops(&foo5, &cmd1); + genl_register_ops(&foo5, &cmd2); + printk("test17: unregister foo5\n"); + genl_unregister_family(&foo5); + /* repeat */ + printk("test18: repeat foo5 register to see if same ID is received\n"); + genl_register_family(&foo5); + genl_register_ops(&foo5, &cmd1); + genl_register_ops(&foo5, &cmd2); + printk("test19: unregister foo\n"); + genl_unregister_family(&foo5); + /* now lets test for a conflict */ + printk("test20: insert foo5 - dynamic ID\n"); + genl_register_family(&foo5); + genl_register_ops(&foo5, &cmd1); + genl_register_ops(&foo5, &cmd2); + printk("test21: insert foo6 which has ID 1 - should conflict\n"); + genl_register_family(&foo6); + printk("test22: remove foo6 - should not be registered\n"); + genl_unregister_family(&foo6); + printk("test40: unregister foo5\n"); + genl_unregister_family(&foo5); + /* now register all so we can poke them from user space .. */ + if (genl_register_family(&foo1)) { + printk("Failed to register foo6\n"); + } else { + /* XXX: check errors */ + genl_register_ops(&foo1, &cmd1); + genl_register_ops(&foo1, &cmd2); + } + +#if 0 + genl_register_family(&foo2); + genl_register_family(&foo3); + genl_register_family(&foo4); + genl_register_family(&foo5); +#endif + if (genl_register_family(&foo6)) { + printk("Failed to register foo6\n"); + } else { + /* XXX: check errors */ + genl_register_ops(&foo6, &cmd6); + genl_register_ops(&foo6, &cmd7); + } + + printk("-----> genl test ending ...\n"); + return 0; +} + +static void __exit foonltest_exit(void) +{ + genl_unregister_family(&foo1); +#if 0 + genl_unregister_family(&foo2); + genl_unregister_family(&foo3); + genl_unregister_family(&foo4); + genl_unregister_family(&foo5); +#endif + genl_unregister_family(&foo6); + + printk("foonltest_exit\n"); +} + +module_init(foonltest_init); +module_exit(foonltest_exit); diff --git a/net/core/mashimaro.c b/net/core/mashimaro.c new file mode 100644 --- /dev/null +++ b/net/core/mashimaro.c @@ -0,0 +1,227 @@ +/* + * MASHIMARO Ask Mr. Mashimaro + * + * Authors: Thomas Graf + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include + +#define MASHIMARO_VERSION 1 + +static struct genl_family mashimaro = { + .id = GENL_ID_GENERATE, + .name = "mashimaro", + .maxattr = MM_ATTR_MAX, + .version = MASHIMARO_VERSION, +}; + +static char *mm_likes[MM_MAX_LIKES]; + +static int mm_fill_info(int id, u32 pid, u32 seq, u32 flags, + struct sk_buff *skb, u8 cmd) +{ + void *hdr; + + hdr = genlmsg_put(skb, pid, seq, mashimaro.id, 0, flags, cmd, + mashimaro.version); + if (hdr == NULL) + return -1; + + NLA_PUT_U32(skb, MM_ATTR_ID, id); + NLA_PUT_STRING(skb, MM_ATTR_VALUE, mm_likes[id]); + + return genlmsg_end(skb, hdr); + +nla_put_failure: + return genlmsg_cancel(skb, hdr); +} + +static struct sk_buff *mm_build_msg(int id, u32 pid, int seq, int cmd) +{ + struct sk_buff *skb; + int err; + + skb = nlmsg_new(NLMSG_GOODSIZE); + if (skb == NULL) + return ERR_PTR(-ENOBUFS); + + err = mm_fill_info(id, pid, seq, 0, skb, cmd); + if (err < 0) { + nlmsg_free(skb); + return ERR_PTR(err); + } + + return skb; +} + +static void mm_event(int event, int id) +{ + struct sk_buff *msg; + + msg = mm_build_msg(id, 0, 0, event); + if (!IS_ERR(msg)) + genlmsg_multicast(msg, 0, mashimaro.id); +} + +static int mm_getattr(struct sk_buff *skb, struct genl_info *info) +{ + struct sk_buff *msg; + u32 id; + + if (info->attrs[MM_ATTR_ID] == NULL) + return -EINVAL; + + id = nla_get_u32(info->attrs[MM_ATTR_ID]); + if (id >= MM_MAX_LIKES) + return -ERANGE; + + if (mm_likes[id] == NULL) + return -ENOENT; + + msg = mm_build_msg(id, info->snd_pid, info->snd_seq, MM_CMD_NEWATTR); + if (IS_ERR(msg)) + return PTR_ERR(msg); + + return genlmsg_unicast(msg, info->snd_pid); +} + +static int mm_dumpattr(struct sk_buff *skb, struct netlink_callback *cb) +{ + int i, skip = cb->args[0]; + + for (i = 0; i < MM_MAX_LIKES; i++) { + if (i < skip || mm_likes[i] == NULL) + continue; + if (mm_fill_info(i, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, + NLM_F_MULTI, skb, MM_CMD_NEWATTR) < 0) + goto errout; + } + +errout: + cb->args[0] = i; + + return skb->len; +} + +static int mm_setattr(struct sk_buff *skb, struct genl_info *info) +{ + u32 id; + char *value; + + if (info->attrs[MM_ATTR_ID] == NULL || + info->attrs[MM_ATTR_VALUE] == NULL) + return -EINVAL; + + id = nla_get_u32(info->attrs[MM_ATTR_ID]); + if (id >= MM_MAX_LIKES) + return -ERANGE; + + if (info->genlhdr->cmd == MM_CMD_NEWATTR && + mm_likes[id]) + return -EEXIST; + + if (mm_likes[id]) + kfree(mm_likes[id]); + + value = nla_data(info->attrs[MM_ATTR_VALUE]); + mm_likes[id] = kstrdup(value, GFP_KERNEL); + if (mm_likes[id] == NULL) + return -ENOBUFS; + + mm_event(MM_CMD_NEWATTR, id); + + return 0; +} + +static int mm_delattr(struct sk_buff *skb, struct genl_info *info) +{ + u32 id; + + if (info->attrs[MM_ATTR_ID] == NULL) + return -EINVAL; + + id = nla_get_u32(info->attrs[MM_ATTR_ID]); + if (id >= MM_MAX_LIKES) + return -ERANGE; + + if (mm_likes[id] == NULL) + return -ENOENT; + + mm_event(MM_CMD_DELATTR, id); + + kfree(mm_likes[id]); + mm_likes[id] = NULL; + + return 0; +} + +static struct nla_policy mm_policy[MM_ATTR_MAX+1] __read_mostly = { + [MM_ATTR_ID] = { .type = NLA_U32 }, + [MM_ATTR_VALUE] = { .type = NLA_STRING }, +}; + +static struct genl_ops mm_ops[] = { + { + .cmd = MM_CMD_GETATTR, + .doit = mm_getattr, + .dumpit = mm_dumpattr, + .policy = mm_policy, + }, + { + .cmd = MM_CMD_SETATTR, + .doit = mm_setattr, + .policy = mm_policy, + }, + { + .cmd = MM_CMD_NEWATTR, + .doit = mm_setattr, + .policy = mm_policy, + }, + { + .cmd = MM_CMD_DELATTR, + .doit = mm_delattr, + .policy = mm_policy, + }, +}; + +static int __init mm_init(void) +{ + int i; + + if (genl_register_family(&mashimaro) < 0) + goto errout; + + for (i = 0; i < ARRAY_SIZE(mm_ops); i++) + if (genl_register_ops(&mashimaro, &mm_ops[i]) < 0) + goto errout_register; + + mm_likes[0] = kstrdup("Hakuna Matata!", GFP_KERNEL); + + printk("Mr. Mashimaro is alive!\n"); + + return 0; + +errout_register: + genl_unregister_family(&mashimaro); +errout: + printk(KERN_ERR "Unable to initialize Mr. Mashimaro\n"); + return -EFAULT; +} + +static void __exit mm_exit(void) +{ + genl_unregister_family(&mashimaro); +} + +module_init(mm_init); +module_exit(mm_exit); diff --git a/net/netlink/Makefile b/net/netlink/Makefile --- a/net/netlink/Makefile +++ b/net/netlink/Makefile @@ -2,4 +2,4 @@ # Makefile for the netlink driver. # -obj-y := af_netlink.o +obj-y := af_netlink.o attr.o genetlink.o diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -58,6 +58,7 @@ #include #include +#include #define Nprintk(a...) #define NLGRPSZ(x) (ALIGN(x, sizeof(unsigned long) * 8) / 8) @@ -427,7 +428,8 @@ static int netlink_release(struct socket spin_lock(&nlk->cb_lock); if (nlk->cb) { - nlk->cb->done(nlk->cb); + if (nlk->cb->done) + nlk->cb->done(nlk->cb); netlink_destroy_callback(nlk->cb); nlk->cb = NULL; } @@ -582,10 +584,12 @@ static int netlink_bind(struct socket *s return 0; netlink_table_grab(); + printk("proto %x before subscribing bitmap %lx trying %x\n",sk->sk_protocol, *(nlk->groups),nladdr->nl_groups); netlink_update_subscriptions(sk, nlk->subscriptions + hweight32(nladdr->nl_groups) - hweight32(nlk->groups[0])); nlk->groups[0] = (nlk->groups[0] & ~0xffffffffUL) | nladdr->nl_groups; + printk("proto %x after subscribing bitmap %lx\n",sk->sk_protocol, *(nlk->groups)); netlink_table_ungrab(); return 0; @@ -710,7 +714,10 @@ int netlink_attachskb(struct sock *sk, s DECLARE_WAITQUEUE(wait, current); if (!timeo) { if (!nlk->pid) + { + printk("netlink_attachskb overrrun\n"); netlink_overrun(sk); + } sock_put(sk); kfree_skb(skb); return -EAGAIN; @@ -836,15 +843,22 @@ static inline int do_one_broadcast(struc { struct netlink_sock *nlk = nlk_sk(sk); int val; + //int tst = 1<<(p->group-1) & *(nlk->groups); + int tst = 1<group & *(nlk->groups); if (p->exclude_sk == sk) goto out; - if (nlk->pid == p->pid || p->group - 1 >= nlk->ngroups || - !test_bit(p->group - 1, nlk->groups)) + //jamal + printk("do_one_broadcast1: groups: %lx & %lx max %lx tst %d\n", + 1<group, *(nlk->groups), nlk->ngroups, tst); + + if (nlk->pid == p->pid || (p->group > nlk->ngroups) || !tst) goto out; + printk("pid %d/%d\n",nlk->pid , p->pid); if (p->failure) { + printk("p->failure overrrun\n"); netlink_overrun(sk); goto out; } @@ -863,10 +877,12 @@ static inline int do_one_broadcast(struc } } if (p->skb2 == NULL) { + printk("skb2 NULL overrrun\n"); netlink_overrun(sk); /* Clone failed. Notify ALL listeners. */ p->failure = 1; } else if ((val = netlink_broadcast_deliver(sk, p->skb2)) < 0) { + printk("failure deliver overrrun\n"); netlink_overrun(sk); } else { p->congested |= val; @@ -903,7 +919,10 @@ int netlink_broadcast(struct sock *ssk, netlink_lock_table(); sk_for_each_bound(sk, node, &nl_table[ssk->sk_protocol].mc_list) + { //jamal + printk("netlink_broadcast grp %x\n",group); do_one_broadcast(sk, &info); + } kfree_skb(skb); @@ -1005,11 +1024,13 @@ static int netlink_setsockopt(struct soc netlink_table_grab(); old = test_bit(val - 1, nlk->groups); subscriptions = nlk->subscriptions - old + new; + printk("proto %x before subscribing bitmap %lx\n",sk->sk_protocol, *(nlk->groups)); if (new) __set_bit(val - 1, nlk->groups); else __clear_bit(val - 1, nlk->groups); netlink_update_subscriptions(sk, subscriptions); + printk("proto %x after subscribing bitmap %lx\n",sk->sk_protocol, *(nlk->groups)); netlink_table_ungrab(); err = 0; break; @@ -1143,6 +1164,9 @@ static int netlink_sendmsg(struct kiocb if (dst_group) { atomic_inc(&skb->users); + if (sk->sk_protocol == NETLINK_GENERIC) { + printk("netlink_sendmsg grp %x\n",dst_group); + } netlink_broadcast(sk, skb, dst_pid, dst_group, GFP_KERNEL); } err = netlink_unicast(sk, skb, dst_pid, msg->msg_flags&MSG_DONTWAIT); @@ -1260,7 +1284,7 @@ netlink_kernel_create(int unit, unsigned nlk->flags |= NETLINK_KERNEL_SOCKET; netlink_table_grab(); - nl_table[unit].groups = groups < 32 ? 32 : groups; + nl_table[unit].groups = groups <= 32 ? 32 : groups; nl_table[unit].module = module; nl_table[unit].registered = 1; netlink_table_ungrab(); @@ -1325,7 +1349,8 @@ static int netlink_dump(struct sock *sk) skb_queue_tail(&sk->sk_receive_queue, skb); sk->sk_data_ready(sk, skb->len); - cb->done(cb); + if (cb->done) + cb->done(cb); nlk->cb = NULL; spin_unlock(&nlk->cb_lock); @@ -1412,6 +1437,53 @@ void netlink_ack(struct sk_buff *in_skb, netlink_unicast(in_skb->sk, skb, NETLINK_CB(in_skb).pid, MSG_DONTWAIT); } +static int netlink_rcv_skb(struct sk_buff *skb, int (*cb)(struct sk_buff *, + struct nlmsghdr *, int *)) +{ + int err; + unsigned int total_len; + struct nlmsghdr *nlh; + + while (skb->len >= nlmsg_total_size(0)) { + nlh = (struct nlmsghdr *) skb->data; + + if (skb->len < nlh->nlmsg_len) + return 0; + + total_len = min(NLMSG_ALIGN(nlh->nlmsg_len), skb->len); + + if (cb(skb, nlh, &err)) { + if (err == 0) + return -1; + netlink_ack(skb, nlh, err); + } else if (nlh->nlmsg_flags & NLM_F_ACK) + netlink_ack(skb, nlh, 0); + + skb_pull(skb, total_len); + } + + return 0; +} + +int netlink_dequeue(struct sock *sk, int (*cb)(struct sk_buff *, + struct nlmsghdr *, int *)) +{ + struct sk_buff *skb; + + while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) { + if (netlink_rcv_skb(skb, cb)) { + if (skb->len) + skb_queue_head(&sk->sk_receive_queue, skb); + else + kfree_skb(skb); + return 1; + } + + kfree_skb(skb); + } + + return 0; +} #ifdef CONFIG_PROC_FS struct nl_seq_iter { @@ -1660,6 +1732,7 @@ out: core_initcall(netlink_proto_init); EXPORT_SYMBOL(netlink_ack); +EXPORT_SYMBOL(netlink_dequeue); EXPORT_SYMBOL(netlink_broadcast); EXPORT_SYMBOL(netlink_dump_start); EXPORT_SYMBOL(netlink_kernel_create); diff --git a/net/netlink/attr.c b/net/netlink/attr.c new file mode 100644 --- /dev/null +++ b/net/netlink/attr.c @@ -0,0 +1,265 @@ +/* + * NETLINK Netlink attributes + * + * Authors: Thomas Graf + * Alexey Kuznetsov + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static u16 nla_attr_minlen[NLA_TYPE_MAX+1] __read_mostly = { + [NLA_U8] = sizeof(u8), + [NLA_U16] = sizeof(u16), + [NLA_U32] = sizeof(u32), + [NLA_U64] = sizeof(u64), + [NLA_STRING] = 1, + [NLA_NESTED] = NLA_HDRLEN, +}; + +static int validate_nla(struct nlattr *nla, int maxtype, + struct nla_policy *policy) +{ + struct nla_policy *pt; + int minlen = 0; + + if (nla->nla_type <= 0 || nla->nla_type > maxtype) + return 0; + + pt = &policy[nla->nla_type]; + + BUG_ON(pt->type > NLA_TYPE_MAX); + + if (pt->minlen) + minlen = pt->minlen; + else if (pt->type != NLA_UNSPEC) + minlen = nla_attr_minlen[pt->type]; + + if (pt->type == NLA_FLAG && nla_len(nla) > 0) + return -ERANGE; + + if (nla_len(nla) < minlen) + return -ERANGE; + + return 0; +} + +/** + * nla_validate - Validate a stream of attributes + * @head: head of attribute stream + * @len: length of attribute stream + * @maxtype: maximum attribute type to be expected + * @policy: validation policy + * + * Validates all attributes in the specified attribute stream + * against the specified policy. Attributes with a type exceeding + * maxtype will be ignored. See documenation of struct nla_policy + * for more details. + * + * Returns 0 on success or a negative error code. + */ +int nla_validate(struct nlattr *head, int len, int maxtype, + struct nla_policy *policy) +{ + struct nlattr *nla; + int rem, err; + + nla_for_each_attr(nla, head, len, rem) { + err = validate_nla(nla, maxtype, policy); + if (err < 0) + goto errout; + } + + err = 0; +errout: + return err; +} + +/** + * nla_parse - Parse a stream of attributes into a tb buffer + * @tb: destination array with maxtype+1 elements + * @maxtype: maximum attribute type to be expected + * @head: head of attribute stream + * @len: length of attribute stream + * + * Parses a stream of attributes and stores a pointer to each attribute in + * the tb array accessable via the attribute type. Attributes with a type + * exceeding maxtype will be silently ignored for backwards compatibility + * reasons. policy may be set to NULL if no validation is required. + * + * Returns 0 on success or a negative error code. + */ +int nla_parse(struct nlattr *tb[], int maxtype, struct nlattr *head, int len, + struct nla_policy *policy) +{ + struct nlattr *nla; + int rem, err; + + memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1)); + + nla_for_each_attr(nla, head, len, rem) { + u16 type = nla->nla_type; + + if (type > 0 && type <= maxtype) { + if (policy) { + err = validate_nla(nla, maxtype, policy); + if (err < 0) + goto errout; + } + + tb[type] = nla; + } + } + + if (unlikely(rem > 0)) + printk(KERN_WARNING "netlink: %d bytes leftover after parsing " + "attributes.\n", rem); + + err = 0; +errout: + return err; +} + +/** + * nla_find - Find a specific attribute in a stream of attributes + * @head: head of attribute stream + * @len: length of attribute stream + * @attrtype: type of attribute to look for + * + * Returns the first attribute in the stream matching the specified type. + */ +struct nlattr *nla_find(struct nlattr *head, int len, int attrtype) +{ + struct nlattr *nla; + int rem; + + nla_for_each_attr(nla, head, len, rem) + if (nla->nla_type == attrtype) + return nla; + + return NULL; +} + +/** + * nla_strlcpy - Copy string attribute payload into a sized buffer + * @dst: where to copy the string to + * @src: attribute to copy the string from + * @dstsize: size of destination buffer + * + * Copies at most dstsize - 1 bytes into the destination buffer. + * The result is always a valid NUL-terminated string. Unlike + * strlcpy the destination buffer is always padded out. + * + * Returns the length of the source buffer. + */ +size_t nla_strlcpy(char *dst, const struct nlattr *nla, size_t dstsize) +{ + size_t srclen = nla_len(nla); + char *src = nla_data(nla); + + if (srclen > 0 && src[srclen - 1] == '\0') + srclen--; + + if (dstsize > 0) { + size_t len = (srclen >= dstsize) ? dstsize - 1 : srclen; + + memset(dst, 0, dstsize); + memcpy(dst, src, len); + } + + return srclen; +} + +/** + * nla_memcpy - Copy a netlink attribute into another memory area + * @dest: where to copy to memcpy + * @src: netlink attribute to copy from + * @count: size of the destination area + * + * Note: The number of bytes copied is limited by the length of + * attribute's payload. memcpy + * + * Returns the number of bytes copied. + */ +int nla_memcpy(void *dest, struct nlattr *src, int count) +{ + int minlen = min_t(int, count, nla_len(src)); + + memcpy(dest, nla_data(src), minlen); + + return minlen; +} + +/** + * nla_memcmp - Compare an attribute with sized memory area + * @nla: netlink attribute + * @data: memory area + * @size: size of memory area + */ +int nla_memcmp(const struct nlattr *nla, const void *data, + size_t size) +{ + int d = nla_len(nla) - size; + + if (d == 0) + d = memcmp(nla_data(nla), data, size); + + return d; +} + +/** + * nla_strcmp - Compare a string attribute against a string + * @nla: netlink string attribute + * @str: another string + */ +int nla_strcmp(const struct nlattr *nla, const char *str) +{ + int len = strlen(str) + 1; + int d = nla_len(nla) - len; + + if (d == 0) + d = memcmp(nla_data(nla), str, len); + + return d; +} + +/** + * __nla_put - Add a netlink attribute to a socket buffer + * @skb: socket buffer to add attribute to + * @attrtype: attribute type + * @attrlen: length of attribute payload + * @data: head of attribute payload + * + * The caller is responsible to ensure that the skb provides enough + * tailroom for the attribute header and payload. + */ +void __nla_put(struct sk_buff *skb, int attrtype, int attrlen, + const void *data) +{ + struct nlattr *nla; + + nla = __nla_reserve(skb, attrtype, attrlen); + memcpy(nla_data(nla), data, attrlen); +} + +EXPORT_SYMBOL(nla_validate); +EXPORT_SYMBOL(nla_parse); +EXPORT_SYMBOL(nla_find); +EXPORT_SYMBOL(nla_strlcpy); +EXPORT_SYMBOL(__nla_put); +EXPORT_SYMBOL(nla_memcpy); +EXPORT_SYMBOL(nla_memcmp); +EXPORT_SYMBOL(nla_strcmp); diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c new file mode 100644 --- /dev/null +++ b/net/netlink/genetlink.c @@ -0,0 +1,640 @@ +/* + * NETLINK Generic Netlink Family + * + * Authors: Jamal Hadi Salim + * Thomas Graf + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct sock *genl_sock = NULL; + +static DECLARE_MUTEX(genl_sem); /* serialization of message processing */ + +static void genl_lock(void) +{ + down(&genl_sem); +} + +static int genl_trylock(void) +{ + return down_trylock(&genl_sem); +} + +static void genl_unlock(void) +{ + up(&genl_sem); + + if (genl_sock && genl_sock->sk_receive_queue.qlen) + genl_sock->sk_data_ready(genl_sock, 0); +} + +#define GENL_FAM_TAB_SIZE 16 +#define GENL_FAM_TAB_MASK (GENL_FAM_TAB_SIZE - 1) + +static struct list_head family_ht[GENL_FAM_TAB_SIZE]; + +static int genl_ctrl_event(int event, void *data); + +static inline unsigned int genl_family_hash(unsigned int id) +{ + return id & GENL_FAM_TAB_MASK; +} + +static inline struct list_head *genl_family_chain(unsigned int id) +{ + return &family_ht[genl_family_hash(id)]; +} + +static struct genl_family *genl_family_find_byid(unsigned int id) +{ + struct genl_family *f; + + list_for_each_entry(f, genl_family_chain(id), family_list) + if (f->id == id) + return f; + + return NULL; +} + +static struct genl_family *genl_family_find_byname(char *name) +{ + struct genl_family *f; + int i; + + for (i = 0; i < GENL_FAM_TAB_SIZE; i++) + list_for_each_entry(f, genl_family_chain(i), family_list) + if (strcmp(f->name, name) == 0) + return f; + + return NULL; +} + +static struct genl_ops *genl_get_cmd(u8 cmd, struct genl_family *family) +{ + struct genl_ops *ops; + + list_for_each_entry(ops, &family->ops_list, ops_list) + if (ops->cmd == cmd) + return ops; + + return NULL; +} + +/* Of course we are going to have problems once we hit + * 2^16 alive types, but that can only happen by year 2K +*/ +static inline u16 genl_generate_id(void) +{ + static u16 id_gen_idx; + int overflowed = 0; + + do { + if (id_gen_idx == 0) + id_gen_idx = GENL_MIN_ID; + + if (++id_gen_idx > GENL_MAX_ID) { + if (!overflowed) { + overflowed = 1; + id_gen_idx = 0; + continue; + } else + return 0; + } + + } while (genl_family_find_byid(id_gen_idx)); + + return id_gen_idx; +} + +/** + * genl_register_ops - register generic netlink operations + * @family: generic netlink family + * @ops: operations to be registered + * + * Registers the specified operations and assigns them to the specified + * family. Either a doit or dumpit callback must be specified or the + * operation will fail. Only one operation structure per command + * identifier may be registered. + * + * See include/net/genetlink.h for more documenation on the operations + * structure. + * + * Returns 0 on success or a negative error code. + */ +int genl_register_ops(struct genl_family *family, struct genl_ops *ops) +{ + int err = -EINVAL; + + if (ops->dumpit == NULL && ops->doit == NULL) + goto errout; + + if (genl_get_cmd(ops->cmd, family)) { + err = -EEXIST; + goto errout; + } + + genl_lock(); + list_add_tail(&ops->ops_list, &family->ops_list); + genl_unlock(); + + genl_ctrl_event(CTRL_CMD_NEWOPS, ops); + err = 0; +errout: + return err; +} + +/** + * genl_unregister_ops - unregister generic netlink operations + * @family: generic netlink family + * @ops: operations to be unregistered + * + * Unregisters the specified operations and unassigns them from the + * specified family. The operation blocks until the current message + * processing has finished and doesn't start again until the + * unregister process has finished. + * + * Note: It is not necessary to unregister all operations before + * unregistering the family, unregistering the family will cause + * all assigned operations to be unregistered automatically. + * + * Returns 0 on success or a negative error code. + */ +int genl_unregister_ops(struct genl_family *family, struct genl_ops *ops) +{ + struct genl_ops *rc; + + genl_lock(); + list_for_each_entry(rc, &family->ops_list, ops_list) { + if (rc == ops) { + list_del(&ops->ops_list); + genl_unlock(); + genl_ctrl_event(CTRL_CMD_DELOPS, ops); + return 0; + } + } + genl_unlock(); + + return -ENOENT; +} + +/** + * genl_register_family - register a generic netlink family + * @family: generic netlink family + * + * Registers the specified family after validating it first. Only one + * family may be registered with the same family name or identifier. + * The family id may equal GENL_ID_GENERATE causing an unique id to + * be automatically generated and assigned. + * + * Return 0 on success or a negative error code. + */ +int genl_register_family(struct genl_family *family) +{ + int err = -EINVAL; + + if (family->id && family->id < GENL_MIN_ID) + goto errout; + + if (family->id > GENL_MAX_ID) + goto errout; + + INIT_LIST_HEAD(&family->ops_list); + + genl_lock(); + + if (genl_family_find_byname(family->name)) { + err = -EEXIST; + goto errout_locked; + } + + if (genl_family_find_byid(family->id)) { + err = -EEXIST; + goto errout_locked; + } + + if (!try_module_get(family->owner)) { + err = -EBUSY; + goto errout_locked; + } + + if (family->id == GENL_ID_GENERATE) { + u16 newid = genl_generate_id(); + + if (!newid) { + err = -ENOMEM; + goto errout_locked; + } + + family->id = newid; + } + + if (family->maxattr) { + family->attrbuf = kmalloc((family->maxattr+1) * + sizeof(struct nlattr *), GFP_KERNEL); + if (family->attrbuf == NULL) { + err = -ENOMEM; + goto errout; + } + } else + family->attrbuf = NULL; + + list_add_tail(&family->family_list, genl_family_chain(family->id)); + genl_unlock(); + + genl_ctrl_event(CTRL_CMD_NEWFAMILY, family); + + return 0; + +errout_locked: + genl_unlock(); +errout: + return err; +} + +/** + * genl_unregister_family - unregister generic netlink family + * @family: generic netlink family + * + * Unregisters the specified family. + * + * Returns 0 on success or a negative error code. + */ +int genl_unregister_family(struct genl_family *family) +{ + struct genl_family *rc; + + genl_lock(); + + list_for_each_entry(rc, genl_family_chain(family->id), family_list) { + if (family->id != rc->id || strcmp(rc->name, family->name)) + continue; + + list_del(&rc->family_list); + INIT_LIST_HEAD(&family->ops_list); + genl_unlock(); + + module_put(family->owner); + kfree(family->attrbuf); + genl_ctrl_event(CTRL_CMD_DELFAMILY, family); + return 0; + } + + genl_unlock(); + + return -ENOENT; +} + +static inline int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, + int *errp) +{ + struct genl_ops *ops; + struct genl_family *family; + struct genl_info info; + struct genlmsghdr *hdr = nlmsg_data(nlh); + int hdrlen, err = -EINVAL; + + if (!(nlh->nlmsg_flags & NLM_F_REQUEST)) + goto ignore; + + if (nlh->nlmsg_type < NLMSG_MIN_TYPE) + goto ignore; + + family = genl_family_find_byid(nlh->nlmsg_type); + if (family == NULL) { + err = -ENOENT; + goto errout; + } + + hdrlen = GENL_HDRLEN + family->hdrsize; + if (nlh->nlmsg_len < nlmsg_msg_size(hdrlen)) + goto errout; + + ops = genl_get_cmd(hdr->cmd, family); + if (ops == NULL) { + err = -EOPNOTSUPP; + goto errout; + } + + if ((ops->flags & GENL_ADMIN_PERM) && security_netlink_recv(skb)) { + err = -EPERM; + goto errout; + } + + if (nlh->nlmsg_flags & NLM_F_DUMP) { + if (ops->dumpit == NULL) { + err = -EOPNOTSUPP; + goto errout; + } + + *errp = err = netlink_dump_start(genl_sock, skb, nlh, + ops->dumpit, NULL); + if (err == 0) + skb_pull(skb, min(NLMSG_ALIGN(nlh->nlmsg_len), + skb->len)); + return -1; + } + + if (ops->doit == NULL) { + err = -EOPNOTSUPP; + goto errout; + } + + if (family->attrbuf) { + err = nlmsg_parse(nlh, hdrlen, family->attrbuf, family->maxattr, + ops->policy); + if (err < 0) + goto errout; + } + + info.snd_seq = nlh->nlmsg_seq; + info.snd_pid = NETLINK_CB(skb).pid; + info.nlhdr = nlh; + info.genlhdr = nlmsg_data(nlh); + info.userhdr = nlmsg_data(nlh) + GENL_HDRLEN; + info.attrs = family->attrbuf; + + *errp = err = ops->doit(skb, &info); + return err; + +ignore: + return 0; + +errout: + *errp = err; + return -1; +} + +static void genl_rcv(struct sock *sk, int len) +{ + do { + if (genl_trylock()) + return; + netlink_dequeue(sk, &genl_rcv_msg); + genl_unlock(); + } while (genl_sock && genl_sock->sk_receive_queue.qlen); +} + +/************************************************************************** + * Controller + **************************************************************************/ + +static int ctrl_fill_info(struct genl_family *family, u32 pid, u32 seq, + u32 flags, struct sk_buff *skb, u8 cmd) +{ + void *hdr; + + hdr = genlmsg_put(skb, pid, seq, GENL_ID_CTRL, 0, flags, cmd, + family->version); + if (hdr == NULL) + return -1; + + NLA_PUT_STRING(skb, CTRL_ATTR_FAMILY_NAME, family->name); + NLA_PUT_U16(skb, CTRL_ATTR_FAMILY_ID, family->id); + + return genlmsg_end(skb, hdr); + +nla_put_failure: + return genlmsg_cancel(skb, hdr); +} + +static int ctrl_dumpfamily(struct sk_buff *skb, struct netlink_callback *cb) +{ + + int i, n = 0; + struct genl_family *rt; + int chains_to_skip = cb->args[0]; + int fams_to_skip = cb->args[1]; + + for (i = 0; i < GENL_FAM_TAB_SIZE; i++) { + if (i < chains_to_skip) + continue; + n = 0; + list_for_each_entry(rt, genl_family_chain(i), family_list) { + if (++n < fams_to_skip) + continue; + if (ctrl_fill_info(rt, NETLINK_CB(cb->skb).pid, + cb->nlh->nlmsg_seq, NLM_F_MULTI, + skb, CTRL_CMD_NEWFAMILY) < 0) + goto errout; + } + + fams_to_skip = 0; + } + +errout: + cb->args[0] = i; + cb->args[1] = n; + + return skb->len; +} + +static struct sk_buff *ctrl_build_msg(struct genl_family *family, u32 pid, + int seq, int cmd) +{ + struct sk_buff *skb; + int err; + + skb = nlmsg_new(NLMSG_GOODSIZE); + if (skb == NULL) + return ERR_PTR(-ENOBUFS); + + err = ctrl_fill_info(family, pid, seq, 0, skb, cmd); + if (err < 0) { + nlmsg_free(skb); + return ERR_PTR(err); + } + + return skb; +} + +static struct nla_policy ctrl_policy[CTRL_ATTR_MAX+1] __read_mostly = { + [CTRL_ATTR_FAMILY_ID] = { .type = NLA_U16 }, + [CTRL_ATTR_FAMILY_NAME] = { .type = NLA_STRING }, +}; + +static int ctrl_getfamily(struct sk_buff *skb, struct genl_info *info) +{ + struct sk_buff *msg; + struct genl_family *res = NULL; + int err = -EINVAL; + + if (info->attrs[CTRL_ATTR_FAMILY_ID]) { + u16 id = nla_get_u16(info->attrs[CTRL_ATTR_FAMILY_ID]); + res = genl_family_find_byid(id); + } + + if (info->attrs[CTRL_ATTR_FAMILY_NAME]) { + char name[GENL_NAMSIZ]; + + if (nla_strlcpy(name, info->attrs[CTRL_ATTR_FAMILY_NAME], + GENL_NAMSIZ) >= GENL_NAMSIZ) + goto errout; + + res = genl_family_find_byname(name); + } + + if (res == NULL) { + err = -ENOENT; + goto errout; + } + + msg = ctrl_build_msg(res, info->snd_pid, info->snd_seq, + CTRL_CMD_NEWFAMILY); + if (IS_ERR(msg)) { + err = PTR_ERR(msg); + goto errout; + } + + err = genlmsg_unicast(msg, info->snd_pid); +errout: + return err; +} + +static int genl_ctrl_event(int event, void *data) +{ + struct sk_buff *msg; + + if (genl_sock == NULL) + return 0; + + switch (event) { + case CTRL_CMD_NEWFAMILY: + case CTRL_CMD_DELFAMILY: + msg = ctrl_build_msg(data, 0, 0, event); + if (IS_ERR(msg)) + return PTR_ERR(msg); + + genlmsg_multicast(msg, 0, GENL_ID_CTRL); + break; + } + + { + struct genl_family *family; + struct genl_ops *ops; + + printk("Controller Event: \n"); + switch (event) { + case CTRL_CMD_NEWFAMILY: + family = data; + printk("\tAdded family id %d name %s \n", + family->id, family->name); + break; + case CTRL_CMD_DELFAMILY: + family = data; + printk("\tDeleted family id %d name %s\n", + family->id, family->name); + break; + case CTRL_CMD_NEWOPS: + ops = data; + printk("\t Added oper cmd %x flags %x\n", + ops->cmd, ops->flags); + break; + case CTRL_CMD_DELOPS: + ops = data; + printk("\t Deleted oper cmd %x flags %x\n", + ops->cmd, ops->flags); + break; + default: + printk("Unknown event\n"); + break; + } + } + + return 0; +} + +static struct genl_ops genl_ctrl_ops = { + .cmd = CTRL_CMD_GETFAMILY, + .doit = ctrl_getfamily, + .dumpit = ctrl_dumpfamily, + .policy = ctrl_policy, +}; + +static struct genl_family genl_ctrl = { + .id = GENL_ID_CTRL, + .name = "nlctrl", + .version = 0x1, + .maxattr = CTRL_ATTR_MAX, + .owner = THIS_MODULE, +}; + +static void genl_dump_ops(struct list_head *list) +{ + struct genl_ops *ops; + int i = 0; + + list_for_each_entry(ops, list, ops_list) + printk("\t ops-%d command %x flags %x\n", i++, ops->cmd, + ops->flags); +} + +static void genl_dump_family(struct genl_family *family) +{ + printk("Family: id %d name %s \n", family->id, family->name); + genl_dump_ops(&family->ops_list); +} + +void genl_dump_all(void) +{ + struct genl_family *family; + int i; + + for (i = 0; i < GENL_FAM_TAB_SIZE; i++) + list_for_each_entry(family, &family_ht[i], family_list) + genl_dump_family(family); +} + +static int __init genl_init(void) +{ + int i, err; + + for (i = 0; i < GENL_FAM_TAB_SIZE; i++) + INIT_LIST_HEAD(&family_ht[i]); + + err = genl_register_family(&genl_ctrl); + if (err < 0) + goto errout; + + err = genl_register_ops(&genl_ctrl, &genl_ctrl_ops); + if (err < 0) + goto errout_register; + + netlink_set_nonroot(NETLINK_GENERIC, NL_NONROOT_RECV); + genl_sock = netlink_kernel_create(NETLINK_GENERIC, GENL_MAX_ID, + genl_rcv, THIS_MODULE); + if (genl_sock == NULL) { + panic("GENL: Cannot initialize generic netlink\n"); + return -ENOMEM; + } + + printk("Generic Netlink interface initialized ..\n"); + return 0; + +errout_register: + genl_unregister_family(&genl_ctrl); +errout: + panic("GENL: Cannot register controller: %d\n", err); + return err; +} + +subsys_initcall(genl_init); +EXPORT_SYMBOL(genl_sock); +EXPORT_SYMBOL(genl_register_ops); +EXPORT_SYMBOL(genl_unregister_ops); +EXPORT_SYMBOL(genl_register_family); +EXPORT_SYMBOL(genl_unregister_family);