基于libuinet的IPv6调试
来源:互联网 发布:客服中心数据分析 编辑:程序博客网 时间:2024/05/21 10:10
基于libuinet的IPv6调试
- •freeBSD的VNET_DEFINE跟SYSCTL_VNET_INT
版权声明:本文为博主原创文章,未经博主允许不得转载。
进来由于工作上的需要,在IPv4之后,要加入IPv6的功能,但是让人蛋疼的是libuinet开源代码中,原作者根本就没有测试IPv6的相关功能,于是蛋疼的花了2个星期,看源码,定位问题。我在调IPv6的时候,主要遇到了3个问题:
1、在打开IPV6的宏,并修改一些编译上的问题时,等编过了之后,开始执行,进程就会一直挂在一个锁上,原因是这个锁是为NULL。(具体代码在哪行已经记不清了,也怪我没有记录问题的习惯,不过跟jail和prison有关,源文件是uinet_kern_jail.c,这个文件是由原作者在kern_jail.c的基础上移植出来的。)这就给了我一个信号,这把锁没有初始化或者说是prison这个模块没有初始化,既然知道原因了,那就简单了,只要到原文件中重新移植一份过来就行。以下是我一直过来的相关代码:
- uinet_kern_jail.c:
- #define DEFAULT_HOSTUUID "00000000-0000-0000-0000-000000000000"
- /* Keep struct prison prison0 and some code in kern_jail_set() readable. */
- #ifdef INET
- #ifdef INET6
- #define _PR_IP_SADDRSEL PR_IP4_SADDRSEL|PR_IP6_SADDRSEL
- #else
- #define _PR_IP_SADDRSEL PR_IP4_SADDRSEL
- #endif
- #else /* !INET */
- #ifdef INET6
- #define _PR_IP_SADDRSEL PR_IP6_SADDRSEL
- #else
- #define _PR_IP_SADDRSEL 0
- #endif
- #endif
- /* prison0 describes what is "real" about the system. */
- struct prison prison0 = {
- .pr_id = 0,
- .pr_name = "0",
- .pr_ref = 1,
- .pr_uref = 1,
- .pr_path = "/",
- .pr_securelevel = -1,
- .pr_devfs_rsnum = 0,
- .pr_childmax = JAIL_MAX,
- .pr_hostuuid = DEFAULT_HOSTUUID,
- .pr_children = LIST_HEAD_INITIALIZER(prison0.pr_children),
- #ifdef VIMAGE
- .pr_flags = PR_HOST|PR_VNET|_PR_IP_SADDRSEL,
- #else
- .pr_flags = PR_HOST|_PR_IP_SADDRSEL,
- #endif
- .pr_allow = PR_ALLOW_ALL,
- };
- MTX_SYSINIT(prison0, &prison0.pr_mtx, "jail mutex", MTX_SPIN)
2、既然运行没有问题了,也不会挂了,那就应该创建一个IPv6的接口让他能ping通,创建IPv6的接口代码可以参照IPv4去写,这一块不难。在我创建了一个IPv6接口之后,因为接口会有ifindex,而我所碰到的应用场景是ifindex为32个位,这其实也没啥问题,因为freeBSD中所使用的ifindex也是32位,但是我不知道装有freeBSD的机子在给它配IPv6的接口时,如果给的ifindex大小大于2个字节,也就是65535,这个ipv6接口是不是还能正常ping通?装有freeBSD机子的同学可以试验一下。我为什么要这么说呢,因为在我给我的接口指定了70001这个ifindex时接口倒是创建成功了,但是在进行DAD检测的时候硬是没有把包发出来,既然如此只能一步一步的去定位了,最后发现数据包在ip6_output这个函数的707行,原因是zone跟dst_sa.sin6_scope_id不相等,于是直接
- goto badscope;
- 既然知道是在这里出的问题,那就继续深入下去查,<pre name="code" class="objc">/*
- * generate standard sockaddr_in6 from embedded form.
- */
- int
- sa6_recoverscope(struct sockaddr_in6 *sin6)
- {
- char ip6buf[INET6_ADDRSTRLEN];
- u_int32_t zoneid;
- if (sin6->sin6_scope_id != 0) {
- log(LOG_NOTICE,
- "sa6_recoverscope: assumption failure (non 0 ID): %s%%%d\n",
- ip6_sprintf(ip6buf, &sin6->sin6_addr), sin6->sin6_scope_id);
- /* XXX: proceed anyway... */
- }
- if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) ||
- IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr)) {
- /*
- * KAME assumption: link id == interface id
- */
- zoneid = ntohs(sin6->sin6_addr.s6_addr16[1]);
- if (zoneid) {
- /* sanity check */
- if (zoneid < 0 || V_if_index < zoneid)
- return (ENXIO);
- if (!ifnet_byindex(zoneid))
- return (ENXIO);
- sin6->sin6_addr.s6_addr16[1] = 0;
- sin6->sin6_scope_id = zoneid;
- }
- }
- return 0;
- }
- 这个函数的主要作用是根据ipv6地址中的<span style="font-family: Arial, Helvetica, sans-serif;">s6_addr16[1]这二个字节来获取接口的ifindex,然后通过这个ifindex来找到ifnet这个结构体,但是很遗憾,肯定是找不到的,因为这里只使用了二个字节,而我们的ifindex时4个字节,这也是为什么我怀疑装有freeBSD的机子其ifindex如果大于65535这个数值,这个接口还能不能好使的理由所在。</span>
- /*
- * Determine the appropriate scope zone ID for in6 and ifp. If ret_id is
- * non NULL, it is set to the zone ID. If the zone ID needs to be embedded
- * in the in6_addr structure, in6 will be modified.
- *
- * ret_id - unnecessary?
- */
- int
- in6_setscope(struct in6_addr *in6, struct ifnet *ifp, u_int32_t *ret_id)
- {
- int scope;
- u_int32_t zoneid = 0;
- struct scope6_id *sid;
- IF_AFDATA_LOCK(ifp);
- sid = SID(ifp);
- #ifdef DIAGNOSTIC
- if (sid == NULL) { /* should not happen */
- panic("in6_setscope: scope array is NULL");
- /* NOTREACHED */
- }
- #endif
- /*
- * special case: the loopback address can only belong to a loopback
- * interface.
- */
- if (IN6_IS_ADDR_LOOPBACK(in6)) {
- if (!(ifp->if_flags & IFF_LOOPBACK)) {
- IF_AFDATA_UNLOCK(ifp);
- return (EINVAL);
- } else {
- if (ret_id != NULL)
- *ret_id = 0; /* there's no ambiguity */
- IF_AFDATA_UNLOCK(ifp);
- return (0);
- }
- }
- scope = in6_addrscope(in6);
- SCOPE6_LOCK();
- switch (scope) {
- case IPV6_ADDR_SCOPE_INTFACELOCAL: /* should be interface index */
- zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL];
- break;
- case IPV6_ADDR_SCOPE_LINKLOCAL:
- zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL];
- break;
- case IPV6_ADDR_SCOPE_SITELOCAL:
- zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL];
- break;
- case IPV6_ADDR_SCOPE_ORGLOCAL:
- zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL];
- break;
- default:
- zoneid = 0; /* XXX: treat as global. */
- break;
- }
- SCOPE6_UNLOCK();
- IF_AFDATA_UNLOCK(ifp);
- if (ret_id != NULL)
- *ret_id = zoneid;
- if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6))
- <pre name="code" class="objc">in6->s6_addr16[1] = htons(zoneid & 0xffff);
/* XXX */return (0);}这个函数的其中一个作用是:如果穿进来的这个IP是linklocal类型的地址,那么它会取出ifp中的ifindex并把这个ifindex赋值给地址的2、3字节,
- in6->s6_addr16[1] = htons(zoneid & 0xffff);
(1),然后在sa6_recoverscope函数里注释掉通过scope去查ifp这个步骤的代码,这个方法我没仔细的往下面去调,我用的是第二种方法,
(2)在in6_setscope中设置ifindex的时候我直接采用4个字节去代替2个字节这种做法,如下
- in6->s6_addr32[1] = htonl(zoneid);
- ip6 = mtod(m, struct ip6_hdr *);
- /* IPv6 source address. */
- scope = in6_getscope(&ip6->ip6_src);/*!< 通过getscope可知,只有linklocal或者intfacelocal才能获得scope */
- w = (u_int16_t *)&ip6->ip6_src;
- sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
- sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
- if (scope != 0)
- sum -= scope;/*!< 因为ipv6中的scope字段是要清除掉的,因此在计算sum的时候就要事先去除 */
- /* IPv6 destination address. */
- scope = in6_getscope(&ip6->ip6_dst);
- w = (u_int16_t *)&ip6->ip6_dst;
- sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
- sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
- if (scope != 0)
- sum -= scope;
- w0 = (u_int32_t *)&ip6->ip6_src;
看到这里,想必你也明白了为什么。因此要解决cksum的问题,那只需要以2个字节为单位来操作,在减去scope的时候,scope高2字节跟低2字节分开来操作就好了。
- sum -= ((scope & 0xffff) + (scope >> 16));
(3)继续说下第三个问题,这个问题描述是在当时ping通了以后,连续ping了4-5个,就会在定时器那里挂掉了,还是空指针,这表明定时器在注册的时候信息不全,这个问题只要在uinet_kern_rwlock.c中加入一下代码即可:
- static void
- lock_rw(struct lock_object *lock, int how)
- {
- struct rwlock *rw;
- rw = (struct rwlock *)lock;
- if (how)
- rw_wlock(rw);
- else
- rw_rlock(rw);
- }
- static int
- unlock_rw(struct lock_object *lock)
- {
- struct rwlock *rw;
- rw = (struct rwlock *)lock;
- rw_assert(rw, RA_LOCKED | LA_NOTRECURSED);
- if (1) {
- rw_runlock(rw);
- return (0);
- } else {
- rw_wunlock(rw);
- return (1);
- }
- }
- struct lock_class lock_class_rw = {
- .lc_name = "rw",
- .lc_flags = LC_SLEEPLOCK | LC_RECURSABLE | LC_UPGRADABLE,
- .lc_assert = assert_rw,
- #ifdef DDB
- .lc_ddb_show = db_show_rwlock,
- #endif
- <span style="white-space:pre"> </span> .lc_lock = lock_rw,
- .lc_unlock = unlock_rw,
- #ifdef KDTRACE_HOOKS
- .lc_owner = owner_rw,
- #endif
- };
- 基于libuinet的IPv6调试
- 基于libuinet的IPv6调试
- 基于Windows 的IPv6 实验
- 基于IPv6的网络程序设计与实践
- 基于ipv6的socket通信(TCP)
- 基于IPv6的语音聊天程序
- 基于IPv6的多播通信
- 配置基于IPv6的单节点Ceph
- 基于IPv6的下一代网络技术的特征分析
- 用浏览器访问基于IPv6地址的服务
- 基于SOCKS的IPv4向IPv6过渡技术
- 基于IPv6的无线传感网异构通信技术研究
- 基于Android系统的IPv6网络接入分析
- 调试cc2530dk/example/udp-ipv6
- 基于网络的内核调试
- 基于AOP的日志调试
- 基于SignalTap的FPGA调试
- 基于DEV的命令行调试
- Codeforces Round #378 (Div. 2) A. Grasshopper And the String
- 到目前为止,Linux下最完整的Samba服务器配置攻略
- hadoop实现文档倒排
- [Java练习]利用map统计一句话中单词的出现次数
- Object-C 数组
- 基于libuinet的IPv6调试
- 11.1
- [JZOJ4838]I Like Matrix!
- 一样是做鸭,绝味与周黑鸭的利润为何相差那么大?
- js平滑滚动到顶部,底部,指定地方
- JZOJ4838. I Like Matrix!
- java.lang.Exception: No tests found matching [{ExactMatcher:fDisplayName=findAll], {ExactMatcher:fDi
- 也不是每个腊鸡搬我帖,删我评论,我都会愤怒的
- hibernate delete update 失效问题