dn_fib.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:16k
源码类别:

嵌入式Linux

开发平台:

Unix_Linux

  1. /*
  2.  * DECnet       An implementation of the DECnet protocol suite for the LINUX
  3.  *              operating system.  DECnet is implemented using the  BSD Socket
  4.  *              interface as the means of communication with the user level.
  5.  *
  6.  *              DECnet Routing Forwarding Information Base (Glue/Info List)
  7.  *
  8.  * Author:      Steve Whitehouse <SteveW@ACM.org>
  9.  *
  10.  *
  11.  * Changes:
  12.  *              Alexey Kuznetsov : SMP locking changes
  13.  *              Steve Whitehouse : Rewrote it... Well to be more correct, I
  14.  *                                 copied most of it from the ipv4 fib code.
  15.  *
  16.  */
  17. #include <linux/config.h>
  18. #include <linux/string.h>
  19. #include <linux/net.h>
  20. #include <linux/socket.h>
  21. #include <linux/sockios.h>
  22. #include <linux/init.h>
  23. #include <linux/skbuff.h>
  24. #include <linux/netlink.h>
  25. #include <linux/rtnetlink.h>
  26. #include <linux/proc_fs.h>
  27. #include <linux/netdevice.h>
  28. #include <linux/timer.h>
  29. #include <linux/spinlock.h>
  30. #include <asm/atomic.h>
  31. #include <asm/uaccess.h>
  32. #include <net/neighbour.h>
  33. #include <net/dst.h>
  34. #include <net/dn.h>
  35. #include <net/dn_route.h>
  36. #include <net/dn_fib.h>
  37. #include <net/dn_neigh.h>
  38. #include <net/dn_dev.h>
  39. #define for_fib_info() { struct dn_fib_info *fi;
  40. for(fi = dn_fib_info_list; fi; fi = fi->fib_next)
  41. #define endfor_fib_info() }
  42. #define for_nexthops(fi) { int nhsel; const struct dn_fib_nh *nh;
  43. for(nhsel = 0, nh = (fi)->fib_nh; nhsel < (fi)->fib_nhs; nh++, nhsel++)
  44. #define change_nexthops(fi) { int nhsel; struct dn_fib_nh *nh;
  45. for(nhsel = 0, nh = (struct dn_fib_nh *)((fi)->fib_nh); nhsel < (fi)->fib_nhs; nh++, nhsel++)
  46. #define endfor_nexthops(fi) }
  47. extern int dn_cache_dump(struct sk_buff *skb, struct netlink_callback *cb);
  48. static struct dn_fib_info *dn_fib_info_list;
  49. static rwlock_t dn_fib_info_lock = RW_LOCK_UNLOCKED;
  50. int dn_fib_info_cnt;
  51. static struct
  52. {
  53. int error;
  54. u8 scope;
  55. } dn_fib_props[RTA_MAX+1] = {
  56. { 0, RT_SCOPE_NOWHERE }, /* RTN_UNSPEC */
  57. { 0, RT_SCOPE_UNIVERSE }, /* RTN_UNICAST */
  58. { 0, RT_SCOPE_HOST }, /* RTN_LOCAL */
  59. { -EINVAL, RT_SCOPE_NOWHERE }, /* RTN_BROADCAST */
  60. { -EINVAL, RT_SCOPE_NOWHERE }, /* RTN_ANYCAST */
  61. { -EINVAL, RT_SCOPE_NOWHERE }, /* RTN_MULTICAST */
  62. { -EINVAL, RT_SCOPE_UNIVERSE }, /* RTN_BLACKHOLE */
  63. { -EHOSTUNREACH, RT_SCOPE_UNIVERSE }, /* RTN_UNREACHABLE */
  64. { -EACCES, RT_SCOPE_UNIVERSE }, /* RTN_PROHIBIT */
  65. { -EAGAIN, RT_SCOPE_UNIVERSE }, /* RTN_THROW */
  66. { -EINVAL, RT_SCOPE_NOWHERE }, /* RTN_NAT */
  67. { -EINVAL, RT_SCOPE_NOWHERE } /* RTN_XRESOLVE */
  68. };
  69. void dn_fib_free_info(struct dn_fib_info *fi)
  70. {
  71. if (fi->fib_dead == 0) {
  72. printk(KERN_DEBUG "DECnet: BUG! Attempt to free alive dn_fib_infon");
  73. return;
  74. }
  75. change_nexthops(fi) {
  76. if (nh->nh_dev)
  77. dev_put(nh->nh_dev);
  78. nh->nh_dev = NULL;
  79. } endfor_nexthops(fi);
  80. dn_fib_info_cnt--;
  81. kfree(fi);
  82. }
  83. void dn_fib_release_info(struct dn_fib_info *fi)
  84. {
  85. write_lock(&dn_fib_info_lock);
  86. if (fi && --fi->fib_treeref == 0) {
  87. if (fi->fib_next)
  88. fi->fib_next->fib_prev = fi->fib_prev;
  89. if (fi->fib_prev)
  90. fi->fib_prev->fib_next = fi->fib_next;
  91. if (fi == dn_fib_info_list)
  92. dn_fib_info_list = fi->fib_next;
  93. fi->fib_dead = 1;
  94. dn_fib_info_put(fi);
  95. }
  96. write_unlock(&dn_fib_info_lock);
  97. }
  98. static __inline__ int dn_fib_nh_comp(const struct dn_fib_info *fi, const struct dn_fib_info *ofi)
  99. {
  100. const struct dn_fib_nh *onh = ofi->fib_nh;
  101. for_nexthops(fi) {
  102. if (nh->nh_oif != onh->nh_oif ||
  103. nh->nh_gw != onh->nh_gw ||
  104. nh->nh_scope != onh->nh_scope ||
  105. nh->nh_weight != onh->nh_weight ||
  106. ((nh->nh_flags^onh->nh_flags)&~RTNH_F_DEAD))
  107. return -1;
  108. onh++;
  109. } endfor_nexthops(fi);
  110. return 0;
  111. }
  112. static __inline__ struct dn_fib_info *dn_fib_find_info(const struct dn_fib_info *nfi)
  113. {
  114. for_fib_info() {
  115. if (fi->fib_nhs != nfi->fib_nhs)
  116. continue;
  117. if (nfi->fib_protocol == fi->fib_protocol &&
  118. nfi->fib_prefsrc == fi->fib_prefsrc &&
  119. nfi->fib_priority == fi->fib_priority &&
  120. ((nfi->fib_flags^fi->fib_flags)&~RTNH_F_DEAD) == 0 &&
  121. (nfi->fib_nhs == 0 || dn_fib_nh_comp(fi, nfi) == 0))
  122. return fi;
  123. } endfor_fib_info();
  124. return NULL;
  125. }
  126. u16 dn_fib_get_attr16(struct rtattr *attr, int attrlen, int type)
  127. {
  128. while(RTA_OK(attr,attrlen)) {
  129. if (attr->rta_type == type)
  130. return *(u16*)RTA_DATA(attr);
  131. attr = RTA_NEXT(attr, attrlen);
  132. }
  133. return 0;
  134. }
  135. static int dn_fib_count_nhs(struct rtattr *rta)
  136. {
  137. int nhs = 0;
  138. struct rtnexthop *nhp = RTA_DATA(rta);
  139. int nhlen = RTA_PAYLOAD(rta);
  140. while(nhlen >= (int)sizeof(struct rtnexthop)) {
  141. if ((nhlen -= nhp->rtnh_len) < 0)
  142. return 0;
  143. nhs++;
  144. nhp = RTNH_NEXT(nhp);
  145. }
  146. return nhs;
  147. }
  148. static int dn_fib_get_nhs(struct dn_fib_info *fi, const struct rtattr *rta, const struct rtmsg *r)
  149. {
  150. struct rtnexthop *nhp = RTA_DATA(rta);
  151. int nhlen = RTA_PAYLOAD(rta);
  152. change_nexthops(fi) {
  153. int attrlen = nhlen - sizeof(struct rtnexthop);
  154. if (attrlen < 0 || (nhlen -= nhp->rtnh_len) < 0)
  155. return -EINVAL;
  156. nh->nh_flags  = (r->rtm_flags&~0xFF) | nhp->rtnh_flags;
  157. nh->nh_oif    = nhp->rtnh_ifindex;
  158. nh->nh_weight = nhp->rtnh_hops + 1;
  159. if (attrlen) {
  160. nh->nh_gw = dn_fib_get_attr16(RTNH_DATA(nhp), attrlen, RTA_GATEWAY);
  161. }
  162. nhp = RTNH_NEXT(nhp);
  163. } endfor_nexthops(fi);
  164. return 0;
  165. }
  166. static int dn_fib_check_nh(const struct rtmsg *r, struct dn_fib_info *fi, struct dn_fib_nh *nh)
  167. {
  168. int err;
  169. if (nh->nh_gw) {
  170. struct dn_fib_key key;
  171. struct dn_fib_res res;
  172. if (nh->nh_flags&RTNH_F_ONLINK) {
  173. struct net_device *dev;
  174. if (r->rtm_scope >= RT_SCOPE_LINK)
  175. return -EINVAL;
  176. if ((dev = __dev_get_by_index(nh->nh_oif)) == NULL)
  177. return -ENODEV;
  178. if (!(dev->flags&IFF_UP))
  179. return -ENETDOWN;
  180. nh->nh_dev = dev;
  181. atomic_inc(&dev->refcnt);
  182. nh->nh_scope = RT_SCOPE_LINK;
  183. return 0;
  184. }
  185. memset(&key, 0, sizeof(key));
  186. key.dst = nh->nh_gw;
  187. key.oif = nh->nh_oif;
  188. key.scope = r->rtm_scope + 1;
  189. if (key.scope < RT_SCOPE_LINK)
  190. key.scope = RT_SCOPE_LINK;
  191. if ((err = dn_fib_lookup(&key, &res)) != 0)
  192. return err;
  193. nh->nh_scope = res.scope;
  194. nh->nh_oif = DN_FIB_RES_OIF(res);
  195. nh->nh_dev = DN_FIB_RES_DEV(res);
  196. if (nh->nh_dev)
  197. atomic_inc(&nh->nh_dev->refcnt);
  198. dn_fib_res_put(&res);
  199. } else {
  200. struct net_device *dev;
  201. if (nh->nh_flags&(RTNH_F_PERVASIVE|RTNH_F_ONLINK))
  202. return -EINVAL;
  203. dev = __dev_get_by_index(nh->nh_oif);
  204. if (dev == NULL || dev->dn_ptr == NULL)
  205. return -ENODEV;
  206. if (!(dev->flags&IFF_UP))
  207. return -ENETDOWN;
  208. nh->nh_dev = dev;
  209. atomic_inc(&nh->nh_dev->refcnt);
  210. nh->nh_scope = RT_SCOPE_HOST;
  211. }
  212. return 0;
  213. }
  214. struct dn_fib_info *dn_fib_create_info(const struct rtmsg *r, struct dn_kern_rta *rta, const struct nlmsghdr *nlh, int *errp)
  215. {
  216. int err;
  217. struct dn_fib_info *fi = NULL;
  218. struct dn_fib_info *ofi;
  219. int nhs = 1;
  220. if (dn_fib_props[r->rtm_type].scope > r->rtm_scope)
  221. goto err_inval;
  222. if (rta->rta_mp) {
  223. nhs = dn_fib_count_nhs(rta->rta_mp);
  224. if (nhs == 0)
  225. goto err_inval;
  226. }
  227. fi = kmalloc(sizeof(*fi)+nhs*sizeof(struct dn_fib_nh), GFP_KERNEL);
  228. err = -ENOBUFS;
  229. if (fi == NULL)
  230. goto failure;
  231. memset(fi, 0, sizeof(*fi)+nhs*sizeof(struct dn_fib_nh));
  232. fi->fib_protocol = r->rtm_protocol;
  233. fi->fib_nhs = nhs;
  234. fi->fib_flags = r->rtm_flags;
  235. if (rta->rta_priority)
  236. fi->fib_priority = *rta->rta_priority;
  237. if (rta->rta_prefsrc)
  238. memcpy(&fi->fib_prefsrc, rta->rta_prefsrc, 2);
  239. if (rta->rta_mp) {
  240. if ((err = dn_fib_get_nhs(fi, rta->rta_mp, r)) != 0)
  241. goto failure;
  242. if (rta->rta_oif && fi->fib_nh->nh_oif != *rta->rta_oif)
  243. goto err_inval;
  244. if (rta->rta_gw && memcmp(&fi->fib_nh->nh_gw, rta->rta_gw, 2))
  245. goto err_inval;
  246. } else {
  247. struct dn_fib_nh *nh = fi->fib_nh;
  248. if (rta->rta_oif)
  249. nh->nh_oif = *rta->rta_oif;
  250. if (rta->rta_gw)
  251. memcpy(&nh->nh_gw, rta->rta_gw, 2);
  252. nh->nh_flags = r->rtm_flags;
  253. nh->nh_weight = 1;
  254. }
  255. if (dn_fib_props[r->rtm_type].error) {
  256. if (rta->rta_gw || rta->rta_oif || rta->rta_mp)
  257. goto err_inval;
  258. goto link_it;
  259. }
  260. if (r->rtm_scope > RT_SCOPE_HOST)
  261. goto err_inval;
  262. if (r->rtm_scope == RT_SCOPE_HOST) {
  263. struct dn_fib_nh *nh = fi->fib_nh;
  264. /* Local address is added */
  265. if (nhs != 1 || nh->nh_gw)
  266. goto err_inval;
  267. nh->nh_scope = RT_SCOPE_NOWHERE;
  268. nh->nh_dev = dev_get_by_index(fi->fib_nh->nh_oif);
  269. err = -ENODEV;
  270. if (nh->nh_dev == NULL)
  271. goto failure;
  272. } else {
  273. change_nexthops(fi) {
  274. if ((err = dn_fib_check_nh(r, fi, nh)) != 0)
  275. goto failure;
  276. } endfor_nexthops(fi)
  277. }
  278. #if I_GET_AROUND_TO_FIXING_PREFSRC
  279. if (fi->fib_prefsrc) {
  280. if (r->rtm_type != RTN_LOCAL || rta->rta_dst == NULL ||
  281.     memcmp(&fi->fib_prefsrc, rta->rta_dst, 2))
  282. if (dn_addr_type(fi->fib_prefsrc) != RTN_LOCAL)
  283. goto err_inval;
  284. }
  285. #endif
  286. link_it:
  287. if ((ofi = dn_fib_find_info(fi)) != NULL) {
  288. fi->fib_dead = 1;
  289. dn_fib_free_info(fi);
  290. ofi->fib_treeref++;
  291. return ofi;
  292. }
  293. fi->fib_treeref++;
  294. atomic_inc(&fi->fib_clntref);
  295. write_lock(&dn_fib_info_lock);
  296. fi->fib_next = dn_fib_info_list;
  297. fi->fib_prev = NULL;
  298. if (dn_fib_info_list)
  299. dn_fib_info_list->fib_prev = fi;
  300. dn_fib_info_list = fi;
  301. dn_fib_info_cnt++;
  302. write_unlock(&dn_fib_info_lock);
  303. return fi;
  304. err_inval:
  305. err = -EINVAL;
  306. failure:
  307. *errp = err;
  308. if (fi) {
  309. fi->fib_dead = 1;
  310. dn_fib_free_info(fi);
  311. }
  312. return NULL;
  313. }
  314. void dn_fib_select_multipath(const struct dn_fib_key *key, struct dn_fib_res *res)
  315. {
  316. struct dn_fib_info *fi = res->fi;
  317. int w;
  318. if (fi->fib_power <= 0) {
  319. int power = 0;
  320. change_nexthops(fi) {
  321. if (!(nh->nh_flags&RTNH_F_DEAD)) {
  322. power += nh->nh_weight;
  323. nh->nh_power = nh->nh_weight;
  324. }
  325. } endfor_nexthops(fi);
  326. fi->fib_power = power;
  327. }
  328. w = jiffies % fi->fib_power;
  329. change_nexthops(fi) {
  330. if (!(nh->nh_flags&RTNH_F_DEAD) && nh->nh_power) {
  331. if ((w -= nh->nh_power) <= 0) {
  332. nh->nh_power--;
  333. fi->fib_power--;
  334. res->nh_sel = nhsel;
  335. return;
  336. }
  337. }
  338. } endfor_nexthops(fi);
  339. printk(KERN_DEBUG "DECnet: BUG! dn_fib_select_multipathn");
  340. }
  341. /*
  342.  * Punt to user via netlink for example, but for now
  343.  * we just drop it.
  344.  */
  345. int dn_fib_rt_message(struct sk_buff *skb)
  346. {
  347. kfree_skb(skb);
  348. return 0;
  349. }
  350. static int dn_fib_check_attr(struct rtmsg *r, struct rtattr **rta)
  351. {
  352. int i;
  353. for(i = 1; i <= RTA_MAX; i++) {
  354. struct rtattr *attr = rta[i-1];
  355. if (attr) {
  356. if (RTA_PAYLOAD(attr) < 4 && RTA_PAYLOAD(attr) != 2)
  357. return -EINVAL;
  358. if (i != RTA_MULTIPATH && i != RTA_METRICS)
  359. rta[i-1] = (struct rtattr *)RTA_DATA(attr);
  360. }
  361. }
  362. return 0;
  363. }
  364. int dn_fib_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
  365. {
  366. struct dn_fib_table *tb;
  367. struct rtattr **rta = arg;
  368. struct rtmsg *r = NLMSG_DATA(nlh);
  369. if (dn_fib_check_attr(r, rta))
  370. return -EINVAL;
  371. tb = dn_fib_get_table(r->rtm_table, 0);
  372. if (tb)
  373. return tb->delete(tb, r, (struct dn_kern_rta *)rta, nlh, &NETLINK_CB(skb));
  374. return -ESRCH;
  375. }
  376. int dn_fib_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
  377. {
  378. struct dn_fib_table *tb;
  379. struct rtattr **rta = arg;
  380. struct rtmsg *r = NLMSG_DATA(nlh);
  381. if (dn_fib_check_attr(r, rta))
  382. return -EINVAL;
  383. tb = dn_fib_get_table(r->rtm_table, 1);
  384. if (tb) 
  385. return tb->insert(tb, r, (struct dn_kern_rta *)rta, nlh, &NETLINK_CB(skb));
  386. return -ENOBUFS;
  387. }
  388. int dn_fib_dump(struct sk_buff *skb, struct netlink_callback *cb)
  389. {
  390. int t;
  391. int s_t;
  392. struct dn_fib_table *tb;
  393. if (NLMSG_PAYLOAD(cb->nlh, 0) >= sizeof(struct rtmsg) &&
  394. ((struct rtmsg *)NLMSG_DATA(cb->nlh))->rtm_flags&RTM_F_CLONED)
  395. return dn_cache_dump(skb, cb);
  396. s_t = cb->args[0];
  397. if (s_t == 0)
  398. s_t = cb->args[0] = DN_MIN_TABLE;
  399. for(t = s_t; t < DN_NUM_TABLES; t++) {
  400. if (t < s_t)
  401. continue;
  402. if (t > s_t)
  403. memset(&cb->args[1], 0, sizeof(cb->args)-sizeof(int));
  404. tb = dn_fib_get_table(t, 0);
  405. if (tb == NULL)
  406. continue;
  407. if (tb->dump(tb, skb, cb) < 0)
  408. break;
  409. }
  410. cb->args[0] = t;
  411. return skb->len;
  412. }
  413. int dn_fib_sync_down(dn_address local, struct net_device *dev, int force)
  414. {
  415.         int ret = 0;
  416.         int scope = RT_SCOPE_NOWHERE;
  417.         if (force)
  418.                 scope = -1;
  419.         for_fib_info() {
  420.                 /* 
  421.                  * This makes no sense for DECnet.... we will almost
  422.                  * certainly have more than one local address the same
  423.                  * over all our interfaces. It needs thinking about
  424.                  * some more.
  425.                  */
  426.                 if (local && fi->fib_prefsrc == local) {
  427.                         fi->fib_flags |= RTNH_F_DEAD;
  428.                         ret++;
  429.                 } else if (dev && fi->fib_nhs) {
  430.                         int dead = 0;
  431.                         change_nexthops(fi) {
  432.                                 if (nh->nh_flags&RTNH_F_DEAD)
  433.                                         dead++;
  434.                                 else if (nh->nh_dev == dev &&
  435.                                                 nh->nh_scope != scope) {
  436.                                         nh->nh_flags |= RTNH_F_DEAD;
  437.                                         fi->fib_power -= nh->nh_power;
  438.                                         nh->nh_power = 0;
  439.                                         dead++;
  440.                                 }
  441.                         } endfor_nexthops(fi)
  442.                         if (dead == fi->fib_nhs) {
  443.                                 fi->fib_flags |= RTNH_F_DEAD;
  444.                                 ret++;
  445.                         }
  446.                 }
  447.         } endfor_fib_info();
  448.         return ret;
  449. }
  450. int dn_fib_sync_up(struct net_device *dev)
  451. {
  452.         int ret = 0;
  453.         if (!(dev->flags&IFF_UP))
  454.                 return 0;
  455.         for_fib_info() {
  456.                 int alive = 0;
  457.                 change_nexthops(fi) {
  458.                         if (!(nh->nh_flags&RTNH_F_DEAD)) {
  459.                                 alive++;
  460.                                 continue;
  461.                         }
  462.                         if (nh->nh_dev == NULL || !(nh->nh_dev->flags&IFF_UP))
  463.                                 continue;
  464.                         if (nh->nh_dev != dev || dev->dn_ptr == NULL)
  465.                                 continue;
  466.                         alive++;
  467.                         nh->nh_power = 0;
  468.                         nh->nh_flags &= ~RTNH_F_DEAD;
  469.                 } endfor_nexthops(fi);
  470.                 if (alive == fi->fib_nhs) {
  471.                         fi->fib_flags &= ~RTNH_F_DEAD;
  472.                         ret++;
  473.                 }
  474.         } endfor_fib_info();
  475.         return ret;
  476. }
  477. void dn_fib_flush(void)
  478. {
  479.         int flushed = 0;
  480.         struct dn_fib_table *tb;
  481.         int id;
  482.         for(id = DN_NUM_TABLES; id > 0; id--) {
  483.                 if ((tb = dn_fib_get_table(id, 0)) == NULL)
  484.                         continue;
  485.                 flushed += tb->flush(tb);
  486.         }
  487.         if (flushed)
  488.                 dn_rt_cache_flush(-1);
  489. }
  490. int dn_fib_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
  491. {
  492. if (!capable(CAP_NET_ADMIN))
  493. return -EPERM;
  494. switch(cmd) {
  495. case SIOCADDRT:
  496. case SIOCDELRT:
  497. return 0;
  498. }
  499. return -EINVAL;
  500. }
  501. #ifdef CONFIG_PROC_FS
  502. static int decnet_rt_get_info(char *buffer, char **start, off_t offset, int length)
  503. {
  504.         int first = offset / 128;
  505.         char *ptr = buffer;
  506.         int count = (length + 127) / 128;
  507.         int len;
  508.         int i;
  509.         struct dn_fib_table *tb;
  510.         *start = buffer + (offset % 128);
  511.         if (--first < 0) {
  512.                 sprintf(buffer, "%-127sn", "IfacetDesttGW  tFlagstRefCnttUsetMetrictMaskttMTUtWindowtIRTT");
  513.                 --count;
  514.                 ptr += 128;
  515.                 first = 0;
  516.         }
  517.         for(i = DN_MIN_TABLE; (i <= DN_NUM_TABLES) && (count > 0); i++) {
  518.                 if ((tb = dn_fib_get_table(i, 0)) != NULL) {
  519.                         int n = tb->get_info(tb, ptr, first, count);
  520.                         count -= n;
  521.                         ptr += n * 128;
  522.                 }
  523.         }
  524.         len = ptr - *start;
  525.         if (len >= length)
  526.                 return length;
  527.         if (len >= 0)
  528.                 return len;
  529.         return 0;
  530. }
  531. #endif /* CONFIG_PROC_FS */
  532. void __exit dn_fib_cleanup(void)
  533. {
  534. proc_net_remove("decnet_route");
  535. dn_fib_table_cleanup();
  536. dn_fib_rules_cleanup();
  537. }
  538. void __init dn_fib_init(void)
  539. {
  540. #ifdef CONFIG_PROC_FS
  541. proc_net_create("decnet_route", 0, decnet_rt_get_info);
  542. #endif
  543. dn_fib_table_init();
  544. dn_fib_rules_init();
  545. }