nefilter


这个函数实现很简单,它就是置事件缓存的标志位。
  1. static inline void
  2. nf_conntrack_event_cache(enum ip_conntrack_events event, struct nf_conn *ct)
  3. {
  4.         struct nf_conntrack_ecache *e;

  5.         if (nf_conntrack_event_cb == NULL)
  6.                 return;

  7.         e = nf_ct_ecache_find(ct);
  8.         if (e == NULL)
  9.                 return;

  10.         set_bit(event, &e->cache);
  11. }
复制代码
问题的关键在于,需要事解Netfilter的event通告机制,简而言知就是,用户态可以使用一个守护进程监控内核连接跟踪的状态,如新建、删除等,在一些特殊应用场合比较有用,如HA环境中。

以下的内容来自于偶一篇没有整理过的笔记,主要分析事件机制的实现,希望对你有所帮助。

//

内核态接口

1、初始化

//事件句柄
  1. static struct nf_ct_event_notifier ctnl_notifier = {
  2.         .fcn = ctnetlink_conntrack_event,
  3. };
复制代码
//初始化,调用nf_conntrack_register_notifier注册
  1. #ifdef CONFIG_NF_CONNTRACK_EVENTS
  2.         ret = nf_conntrack_register_notifier(&ctnl_notifier);
  3.         if (ret < 0) {
  4.                 printk("ctnetlink_init: cannot register notifier.\n");
  5.                 goto err_unreg_exp_subsys;
  6.         }
复制代码
//注销nf_conntrack_unregister_notifier
  1. #ifdef CONFIG_NF_CONNTRACK_EVENTS
  2.         nf_conntrack_unregister_notifier(&ctnl_notifier);
  3. #endif
复制代码
//注册函数
  1. int nf_conntrack_register_notifier(struct nf_ct_event_notifier *new)
  2. {
  3.         int ret = 0;
  4.         struct nf_ct_event_notifier *notify;
  5.         
  6.         //获得锁
  7.         mutex_lock(&nf_ct_ecache_mutex);
  8.         //获取事件句柄,判断是否已注册
  9.         notify = rcu_dereference(nf_conntrack_event_cb);
  10.         if (notify != NULL) {
  11.                 ret = -EBUSY;
  12.                 goto out_unlock;
  13.         }
  14.         //注册新的句柄
  15.         rcu_assign_pointer(nf_conntrack_event_cb, new);
  16.         //释放锁
  17.         mutex_unlock(&nf_ct_ecache_mutex);
  18.         return ret;

  19. out_unlock:
  20.         mutex_unlock(&nf_ct_ecache_mutex);
  21.         return ret;
  22. }
复制代码
//注销函数置相应的指针位
  1. void nf_conntrack_unregister_notifier(struct nf_ct_event_notifier *new)
  2. {
  3.         struct nf_ct_event_notifier *notify;

  4.         mutex_lock(&nf_ct_ecache_mutex);
  5.         notify = rcu_dereference(nf_conntrack_event_cb);
  6.         BUG_ON(notify != new);
  7.         rcu_assign_pointer(nf_conntrack_event_cb, NULL);
  8.         mutex_unlock(&nf_ct_ecache_mutex);
  9. }
复制代码
2、触发事件

事件类型,事件名称是位掩码,设置某个事件,使用 1 << event
  1. /* Connection tracking event types */
  2. enum ip_conntrack_events
  3. {
  4.         IPCT_NEW                = 0,        /* new conntrack */
  5.         IPCT_RELATED                = 1,        /* related conntrack */
  6.         IPCT_DESTROY                = 2,        /* destroyed conntrack */
  7.         IPCT_STATUS                = 3,        /* status has changed */
  8.         IPCT_PROTOINFO                = 4,        /* protocol information has changed */
  9.         IPCT_HELPER                = 5,        /* new helper has been set */
  10.         IPCT_MARK                = 6,        /* new mark has been set */
  11.         IPCT_NATSEQADJ                = 7,        /* NAT is doing sequence adjustment */
  12.         IPCT_SECMARK                = 8,        /* new security mark has been set */
  13. };
复制代码
一个触发场景:
  1. static void death_by_timeout(unsigned long ul_conntrack)
  2. {
  3.         ……

  4.         if (!test_bit(IPS_DYING_BIT, &ct->status) &&
  5.             unlikely(nf_conntrack_event(IPCT_DESTROY, ct) < 0)) {
  6.         ……
复制代码
nf_conntrack_event接口触发事件:
  1. static inline int
  2. nf_conntrack_event(enum ip_conntrack_events event, struct nf_conn *ct)
  3. {
  4.         //计算位掩码后,调用nf_conntrack_eventmask_report,pid和report置为0
  5.         return nf_conntrack_eventmask_report(1 << event, ct, 0, 0);
  6. }
复制代码
  1. static inline int
  2. nf_conntrack_eventmask_report(unsigned int eventmask,
  3.                               struct nf_conn *ct,
  4.                               u32 pid,
  5.                               int report)
  6. {
  7.         int ret = 0;
  8.         struct net *net = nf_ct_net(ct);
  9.         struct nf_ct_event_notifier *notify;
  10.         struct nf_conntrack_ecache *e;

  11.         rcu_read_lock();
  12.         //取得事件通知句柄
  13.         notify = rcu_dereference(nf_conntrack_event_cb);
  14.         if (notify == NULL)
  15.                 goto out_unlock;

  16.         //取得sysctl控制标志
  17.         if (!net->ct.sysctl_events)
  18.                 goto out_unlock;

  19.         //寻找事件缓存
  20.         e = nf_ct_ecache_find(ct);
  21.         if (e == NULL)
  22.                 goto out_unlock;

  23.         if (nf_ct_is_confirmed(ct) && !nf_ct_is_dying(ct)) {
  24.                 //构建nf_ct_event
  25.                 struct nf_ct_event item = {
  26.                         .ct         = ct,
  27.                         .pid        = e->pid ? e->pid : pid,                //根据缓存中的pid,如果没有,使用参数的值
  28.                         .report = report
  29.                 };
  30.                 /* This is a resent of a destroy event? If so, skip missed */
  31.                 unsigned long missed = e->pid ? 0 : e->missed;

  32.                 //调用通知函数
  33.                 ret = notify->fcn(eventmask | missed, &item);
  34.                 if (unlikely(ret < 0 || missed)) {
  35.                         spin_lock_bh(&ct->lock);
  36.                         if (ret < 0) {
  37.                                 /* This is a destroy event that has been
  38.                                  * triggered by a process, we store the PID
  39.                                  * to include it in the retransmission. */
  40.                                 if (eventmask & (1 << IPCT_DESTROY) &&
  41.                                     e->pid == 0 && pid != 0)
  42.                                         e->pid = pid;
  43.                                 else
  44.                                         e->missed |= eventmask;
  45.                         } else
  46.                                 e->missed &= ~missed;
  47.                         spin_unlock_bh(&ct->lock);
  48.                 }
  49.         }
  50. out_unlock:
  51.         rcu_read_unlock();
  52.         return ret;
  53. }
复制代码
  1. static int
  2. ctnetlink_conntrack_event(unsigned int events, struct nf_ct_event *item)
  3. {
  4.         //根据事件掩码,计算相应的nfnetlink conntrack子系统的消息类型和nfnetlink group
  5.         if (events & (1 << IPCT_DESTROY)) {
  6.                 type = IPCTNL_MSG_CT_DELETE;
  7.                 group = NFNLGRP_CONNTRACK_DESTROY;
  8.         } else  if (events & ((1 << IPCT_NEW) | (1 << IPCT_RELATED))) {
  9.                 type = IPCTNL_MSG_CT_NEW;
  10.                 flags = NLM_F_CREATE|NLM_F_EXCL;
  11.                 group = NFNLGRP_CONNTRACK_NEW;
  12.         } else  if (events) {
  13.                 type = IPCTNL_MSG_CT_NEW;
  14.                 group = NFNLGRP_CONNTRACK_UPDATE;
  15.         } else
  16.                 return 0;
  17.                         
  18.         //如果没有置report标志,则需要有相应的监听者
  19.         if (!item->report && !nfnetlink_has_listeners(group))
  20.                 return 0;
  21.         
  22.         //构造skb
  23.         ……
  24.         
  25.         //发送报文
  26.         err = nfnetlink_send(skb, item->pid, group, item->report, GFP_ATOMIC);
  27.         if (err == -ENOBUFS || err == -EAGAIN)
  28.                 return -ENOBUFS;                
  29. }
复制代码
  1. int nfnetlink_send(struct sk_buff *skb, u32 pid,
  2.                    unsigned group, int echo, gfp_t flags)
  3. {
  4.         return nlmsg_notify(nfnl, skb, pid, group, echo, flags);
  5. }
复制代码
  1. int nfnetlink_has_listeners(unsigned int group)
  2. {
  3.         return netlink_has_listeners(nfnl, group);
  4. }
复制代码
  1. nfnetlink group:
  2.         
  3. enum nfnetlink_groups {
  4.         NFNLGRP_NONE,
  5. #define NFNLGRP_NONE                        NFNLGRP_NONE
  6.         NFNLGRP_CONNTRACK_NEW,
  7. #define NFNLGRP_CONNTRACK_NEW                NFNLGRP_CONNTRACK_NEW
  8.         NFNLGRP_CONNTRACK_UPDATE,
  9. #define NFNLGRP_CONNTRACK_UPDATE        NFNLGRP_CONNTRACK_UPDATE
  10.         NFNLGRP_CONNTRACK_DESTROY,
  11. #define NFNLGRP_CONNTRACK_DESTROY        NFNLGRP_CONNTRACK_DESTROY
  12.         NFNLGRP_CONNTRACK_EXP_NEW,
  13. #define        NFNLGRP_CONNTRACK_EXP_NEW        NFNLGRP_CONNTRACK_EXP_NEW
  14.         NFNLGRP_CONNTRACK_EXP_UPDATE,
  15. #define NFNLGRP_CONNTRACK_EXP_UPDATE        NFNLGRP_CONNTRACK_EXP_UPDATE
  16.         NFNLGRP_CONNTRACK_EXP_DESTROY,
  17. #define NFNLGRP_CONNTRACK_EXP_DESTROY        NFNLGRP_CONNTRACK_EXP_DESTROY
  18.         __NFNLGRP_MAX,
  19. };
  20. #define NFNLGRP_MAX        (__NFNLGRP_MAX - 1)
复制代码
//

与用户态的nfnetlink groups不同的是,用户态是一个位图,这是因为,内核是事件的发送者,它一个时刻只
发送一个事件,而用户态是接受者,它可以同时监听多个事件:
  1. /* nfnetlink groups: Up to 32 maximum */
  2. #define NF_NETLINK_CONNTRACK_NEW                 0x00000001
  3. #define NF_NETLINK_CONNTRACK_UPDATE                0x00000002
  4. #define NF_NETLINK_CONNTRACK_DESTROY                0x00000004
  5. #define NF_NETLINK_CONNTRACK_EXP_NEW                0x00000008
  6. #define NF_NETLINK_CONNTRACK_EXP_UPDATE                0x00000010
  7. #define NF_NETLINK_CONNTRACK_EXP_DESTROY        0x00000020
复制代码
特别地,宏NFCT_ALL_CT_GROUPS表示了以上所有的组(不包含EXP_XXX):
  1. #define NFCT_ALL_CT_GROUPS (NF_NETLINK_CONNTRACK_NEW|NF_NETLINK_CONNTRACK_UPDATE|NF_NETLINK_CONNTRACK_DESTROY)
复制代码
//在调用nfct_open,使用subscriptions来描述nfnetlink groups:
  1.                 if (options & CT_OPT_EVENT_MASK)
  2.                         cth = nfct_open(CONNTRACK,
  3.                                         event_mask & NFCT_ALL_CT_GROUPS);
  4.                 else
  5.                         cth = nfct_open(CONNTRACK, NFCT_ALL_CT_GROUPS);

  6.                 nfct_callback_register(cth, NFCT_T_ALL, event_cb, obj);
  7.                 res = nfct_catch(cth);
复制代码
  1. struct nfct_handle *nfct_open_nfnl(struct nfnl_handle *nfnlh,
  2.                                    u_int8_t subsys_id,
  3.                                    unsigned int subscriptions)
  4. {
  5.         ……                                        
  6.         if (subsys_id == 0 || subsys_id == NFNL_SUBSYS_CTNETLINK) {
  7.                 cth->nfnlssh_ct = nfnl_subsys_open(cth->nfnlh, 
  8.                                                    NFNL_SUBSYS_CTNETLINK, 
  9.                                                    IPCTNL_MSG_MAX,
  10.                                                    subscriptions);
  11.                 if (!cth->nfnlssh_ct)
  12.                         goto out_free;
  13.         }
  14.         ……
复制代码
  1. struct nfnl_subsys_handle *
  2. nfnl_subsys_open(struct nfnl_handle *nfnlh, u_int8_t subsys_id,
  3.                  u_int8_t cb_count, u_int32_t subscriptions)
  4. {
  5.         ……
  6.         //在子系统句柄中保存关心的nfnetlink groups
  7.         ssh->subscriptions = subscriptions;
  8.         ……
  9.         /* although now we have nfnl_join to subscribe to certain
  10.          * groups, just keep this to ensure compatibility */
  11.         if (recalc_rebind_subscriptions(nfnlh) < 0) {
  12.                 free(ssh->cb);
  13.                 ssh->cb = NULL;
  14.                 return NULL;
  15.         }
  16.         ……
  17. }
复制代码
recalc_rebind_subscriptions函数绑定套接字,即缓定nfnetlink_group:
  1. static int recalc_rebind_subscriptions(struct nfnl_handle *nfnlh)
  2. {
  3.         int i, err;
  4.         u_int32_t new_subscriptions = nfnlh->subscriptions;

  5.         for (i = 0; i < NFNL_MAX_SUBSYS; i++)
  6.                 new_subscriptions |= nfnlh->subsys[i].subscriptions;

  7.         nfnlh->local.nl_groups = new_subscriptions;
  8.         err = bind(nfnlh->fd, (struct sockaddr *)&nfnlh->local,
  9.                    sizeof(nfnlh->local));
  10.         if (err == -1)
  11.                 return -1;

  12.         nfnlh->subscriptions = new_subscriptions;

  13.         return 0;
  14. }
复制代码
事件回调函数:
  1. static int event_cb(enum nf_conntrack_msg_type type,
  2.                     struct nf_conntrack *ct,
  3.                     void *data)
  4. {
  5.         char buf[1024];
  6.         struct nf_conntrack *obj = data;
  7.         unsigned int op_type = NFCT_O_DEFAULT;
  8.         unsigned int op_flags = 0;

  9.         if (filter_nat(obj, ct))
  10.                 return NFCT_CB_CONTINUE;

  11.         if (options & CT_COMPARISON &&
  12.             !nfct_cmp(obj, ct, NFCT_CMP_ALL | NFCT_CMP_MASK))
  13.                 return NFCT_CB_CONTINUE;

  14.         if (output_mask & _O_XML) {
  15.                 op_type = NFCT_O_XML;
  16.                 if (dump_xml_header_done) {
  17.                         dump_xml_header_done = 0;
  18.                         printf("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
  19.                                "<conntrack>\n");
  20.                 }
  21.         } 
  22.         if (output_mask & _O_EXT)
  23.                 op_flags = NFCT_OF_SHOW_LAYER3;
  24.         if (output_mask & _O_TMS) {
  25.                 if (!(output_mask & _O_XML)) {
  26.                         struct timeval tv;
  27.                         gettimeofday(&tv, NULL);
  28.                         printf("[%-8ld.%-6ld]\t", tv.tv_sec, tv.tv_usec);
  29.                 } else
  30.                         op_flags |= NFCT_OF_TIME;
  31.         }
  32.         if (output_mask & _O_ID)
  33.                 op_flags |= NFCT_OF_ID;

  34.         nfct_snprintf(buf, sizeof(buf), ct, type, op_type, op_flags);

  35.         printf("%s\n", buf);
  36.         fflush(stdout);

  37.         counter++;

  38.         return NFCT_CB_CONTINUE;
  39. }
复制代码
与nfnetlink groups对应的消息类型:
  1. /* message type */
  2. enum nf_conntrack_msg_type {
  3.         NFCT_T_UNKNOWN = 0,

  4.         NFCT_T_NEW_BIT = 0,
  5.         NFCT_T_NEW = (1 << NFCT_T_NEW_BIT),

  6.         NFCT_T_UPDATE_BIT = 1,
  7.         NFCT_T_UPDATE = (1 << NFCT_T_UPDATE_BIT),

  8.         NFCT_T_DESTROY_BIT = 2,
  9.         NFCT_T_DESTROY = (1 << NFCT_T_DESTROY_BIT),

  10.         NFCT_T_ALL = NFCT_T_NEW | NFCT_T_UPDATE | NFCT_T_DESTROY,

  11.         NFCT_T_ERROR_BIT = 31,
  12.         NFCT_T_ERROR = (1 << NFCT_T_ERROR_BIT),
  13. };
复制代码
这个消息类型是在回调函数中,通过__parse_message_type函数取得的:
  1. int __callback(struct nlmsghdr *nlh, struct nfattr *nfa[], void *data)
  2. {
  3.         int ret = NFNL_CB_STOP;
  4.         unsigned int type;
  5.         struct nf_conntrack *ct;
  6.         int len = nlh->nlmsg_len;
  7.         struct __data_container *container = data;

  8.         len -= NLMSG_LENGTH(sizeof(struct nfgenmsg));
  9.         if (len < 0)
  10.                 return NFNL_CB_CONTINUE;

  11.         type = __parse_message_type(nlh);
  12.         ……       
复制代码
主要根据netlink消息首部中的nlmsg_type和nlmsg_flags来取得的:
  1. int __parse_message_type(const struct nlmsghdr *nlh)
  2. {
  3.         u_int16_t type = NFNL_MSG_TYPE(nlh->nlmsg_type);
  4.         u_int16_t flags = nlh->nlmsg_flags;
  5.         int ret = NFCT_T_UNKNOWN;

  6.         if (type == IPCTNL_MSG_CT_NEW) {
  7.                 if (flags & (NLM_F_CREATE|NLM_F_EXCL))
  8.                         ret = NFCT_T_NEW;
  9.                 else
  10.                         ret = NFCT_T_UPDATE;
  11.         } else if (type == IPCTNL_MSG_CT_DELETE)
  12.                 ret = NFCT_T_DESTROY;

  13.         return ret;
  14. }
复制代码
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值