{"id":46960,"date":"2025-07-30T04:57:35","date_gmt":"2025-07-29T20:57:35","guid":{"rendered":"https:\/\/www.wsisp.com\/helps\/46960.html"},"modified":"2025-07-30T04:57:35","modified_gmt":"2025-07-29T20:57:35","slug":"linux%e5%86%85%e6%a0%b8ipv4%e5%a4%9a%e6%92%ad%e8%b7%af%e7%94%b1%e6%b7%b1%e5%ba%a6%e8%a7%a3%e6%9e%90%ef%bc%9a%e4%bb%8e%e6%95%b0%e6%8d%ae%e7%bb%93%e6%9e%84%e5%88%b0%e9%ab%98%e6%95%88%e8%bd%ac%e5%8f%91","status":"publish","type":"post","link":"https:\/\/www.wsisp.com\/helps\/46960.html","title":{"rendered":"Linux\u5185\u6838IPv4\u591a\u64ad\u8def\u7531\u6df1\u5ea6\u89e3\u6790\uff1a\u4ece\u6570\u636e\u7ed3\u6784\u5230\u9ad8\u6548\u8f6c\u53d1"},"content":{"rendered":"<p>\u591a\u64ad\u8def\u7531\u662f\u7f51\u7edc\u901a\u4fe1\u7684\u6838\u5fc3\u6280\u672f\u4e4b\u4e00&#xff0c;Linux\u5185\u6838\u901a\u8fc7\u7cbe\u5bc6\u7684\u591a\u5c42\u8bbe\u8ba1\u5b9e\u73b0\u4e86\u9ad8\u6027\u80fd\u7684IPv4\u591a\u64ad\u8def\u7531\u529f\u80fd\u3002\u672c\u6587\u5c06\u6df1\u5165\u5256\u6790\u5176\u6838\u5fc3\u5b9e\u73b0\u673a\u5236&#xff0c;\u63ed\u793a\u5176\u9ad8\u6548\u8fd0\u8f6c\u7684\u79d8\u5bc6\u3002<\/p>\n<hr \/>\n<h4>\u4e00\u3001\u6838\u5fc3\u6570\u636e\u7ed3\u6784&#xff1a;\u8def\u7531\u7cfb\u7edf\u7684\u57fa\u77f3<\/h4>\n<h5>1. \u591a\u64ad\u8def\u7531\u8868&#xff08;struct mr_table&#xff09;<\/h5>\n<p>struct mr_table {<br \/>\n    struct list_head list;<br \/>\n    struct rhltable mfc_hash;   \/\/ MFC\u54c8\u5e0c\u8868<br \/>\n    struct list_head mfc_cache_list;<br \/>\n    struct list_head mfc_unres_queue; \/\/ \u672a\u89e3\u6790\u961f\u5217<\/p>\n<p>    struct vif_device vif_table[MAXVIFS]; \/\/ \u865a\u62df\u63a5\u53e3\u8868<br \/>\n    int maxvif;                  \/\/ \u5f53\u524d\u6700\u5927VIF\u7d22\u5f15<\/p>\n<p>    atomic_t cache_resolve_queue_len; \/\/ \u672a\u89e3\u6790\u961f\u5217\u957f\u5ea6<br \/>\n    struct timer_list ipmr_expire_timer; \/\/ \u8d85\u65f6\u5b9a\u65f6\u5668<br \/>\n};<\/p>\n<p>\u8def\u7531\u8868\u901a\u8fc7\u53cc\u7ed3\u6784\u5b58\u50a8\u7b56\u7565\u5b9e\u73b0\u9ad8\u6548\u68c0\u7d22&#xff1a;\u54c8\u5e0c\u8868\u7528\u4e8e\u5feb\u901f\u67e5\u627e&#xff0c;\u94fe\u8868\u7528\u4e8e\u987a\u5e8f\u904d\u5386\u3002\u672a\u89e3\u6790\u961f\u5217\u5219\u4e34\u65f6\u5b58\u653e\u7b49\u5f85\u8def\u7531\u51b3\u7b56\u7684\u6570\u636e\u5305\u3002<\/p>\n<h5>2. \u865a\u62df\u63a5\u53e3&#xff08;struct vif_device&#xff09;<\/h5>\n<p>struct vif_device {<br \/>\n    struct net_device *dev;      \/\/ \u5173\u8054\u7684\u7f51\u7edc\u8bbe\u5907<br \/>\n    unsigned long bytes_in, bytes_out; \/\/ \u6d41\u91cf\u7edf\u8ba1<br \/>\n    unsigned long pkt_in, pkt_out;<br \/>\n    __be32 local, remote;       \/\/ \u96a7\u9053\u7aef\u70b9\u5730\u5740<br \/>\n    int flags;                   \/\/ \u7c7b\u578b\u6807\u8bb0<br \/>\n};<\/p>\n<p>\u652f\u6301\u4e09\u79cd\u63a5\u53e3\u7c7b\u578b&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u7269\u7406\u63a5\u53e3&#xff1a;\u76f4\u63a5\u7ed1\u5b9a\u7f51\u7edc\u8bbe\u5907<\/p>\n<\/li>\n<li>\n<p>\u96a7\u9053\u63a5\u53e3&#xff08;VIFF_TUNNEL&#xff09;&#xff1a;\u7528\u4e8e\u8de8\u7f51\u7edc\u8f6c\u53d1<\/p>\n<\/li>\n<li>\n<p>\u6ce8\u518c\u63a5\u53e3&#xff08;VIFF_REGISTER&#xff09;&#xff1a;\u4e0e\u7528\u6237\u6001\u5b88\u62a4\u8fdb\u7a0b\u901a\u4fe1<\/p>\n<\/li>\n<\/ul>\n<h5>3. \u591a\u64ad\u8f6c\u53d1\u7f13\u5b58&#xff08;struct mfc_cache&#xff09;<\/h5>\n<p>struct mfc_cache {<br \/>\n    struct mfc_common _c;<br \/>\n    __be32 mfc_origin;           \/\/ \u6e90\u5730\u5740<br \/>\n    __be32 mfc_mcastgrp;         \/\/ \u7ec4\u5730\u5740<br \/>\n    struct mfc_cache_cmp_arg cmparg; \/\/ \u54c8\u5e0c\u6bd4\u8f83\u53c2\u6570<br \/>\n};<\/p>\n<p>\u5173\u952e\u5b57\u6bb5res.ttls[MAXVIFS]\u5b58\u50a8\u6bcf\u4e2a\u51fa\u53e3\u7684TTL\u9608\u503c&#xff0c;\u5b9e\u73b0\u7cbe\u7ec6\u5316\u7684\u51fa\u53e3\u63a7\u5236\u3002<\/p>\n<hr \/>\n<h4>\u4e8c\u3001\u6570\u636e\u5305\u8f6c\u53d1\u6d41\u7a0b&#xff1a;\u901f\u5ea6\u4e0e\u7cbe\u786e\u7684\u5e73\u8861<\/h4>\n<h5>\u8f6c\u53d1\u8def\u5f84&#xff08;ip_mr_forward&#xff09;<\/h5>\n<p style=\"text-align:center\"><img loading=\"lazy\" decoding=\"async\" alt=\"\" height=\"625\" src=\"https:\/\/www.wsisp.com\/helps\/wp-content\/uploads\/2025\/07\/20250729205726-688935b62982b.png\" width=\"347\" \/><\/p>\n<p>graph TD<br \/>\n    A[\u6570\u636e\u5305\u5230\u8fbe] &#8211;&gt; B{\u5339\u914dMFC?}<br \/>\n    B &#8211;&gt;|\u662f| C[\u6821\u9a8c\u8f93\u5165\u63a5\u53e3]<br \/>\n    B &#8211;&gt;|\u5426| D[\u52a0\u5165\u672a\u89e3\u6790\u961f\u5217]<br \/>\n    C &#8211;&gt; E[\u904d\u5386\u6240\u6709\u51fa\u53e3]<br \/>\n    E &#8211;&gt; F{TTL\u6709\u6548?}<br \/>\n    F &#8211;&gt;|\u662f| G[\u514b\u9686\u5e76\u53d1\u9001]<br \/>\n    F &#8211;&gt;|\u5426| H[\u8df3\u8fc7\u63a5\u53e3]<br \/>\n    G &#8211;&gt; I[\u66f4\u65b0\u7edf\u8ba1\u4fe1\u606f]<\/p>\n<h5>\u5173\u952e\u4f18\u5316\u70b9&#xff1a;<\/h5>\n<li>\n<p>\u5feb\u901f\u514b\u9686\u673a\u5236&#xff1a;\u5bf9\u6bcf\u4e2a\u6709\u6548\u51fa\u53e3\u53ea\u514b\u9686\u5305\u5934&#xff0c;\u907f\u514d\u6570\u636e\u590d\u5236<\/p>\n<p>if (psend !&#061; -1) {<br \/>\n    struct sk_buff *skb2 &#061; skb_clone(skb, GFP_ATOMIC);<br \/>\n    ipmr_queue_xmit(&#8230;, skb2, &#8230;);<br \/>\n}\n<\/li>\n<li>\n<p>\u667a\u80fdTTL\u7ba1\u7406&#xff1a;\u8df3\u8fc7TTL\u4e0d\u8db3\u7684\u63a5\u53e3&#xff0c;\u51cf\u5c11\u65e0\u6548\u64cd\u4f5c<\/p>\n<\/li>\n<li>\n<p>\u7edf\u8ba1\u539f\u5b50\u66f4\u65b0&#xff1a;\u65e0\u9501\u66f4\u65b0\u8ba1\u6570\u5668\u786e\u4fdd\u9ad8\u6027\u80fd<\/p>\n<\/li>\n<hr \/>\n<h4>\u4e09\u3001\u63a7\u5236\u5e73\u9762&#xff1a;\u52a8\u6001\u8def\u7531\u7ba1\u7406<\/h4>\n<h5>1. MFC\u751f\u547d\u5468\u671f\u7ba1\u7406<\/h5>\n<ul>\n<li>\n<p>\u52a8\u6001\u5b66\u4e60&#xff1a;\u901a\u8fc7PIM\/IGMP\u6d88\u606f\u81ea\u52a8\u521b\u5efa\u6761\u76ee<\/p>\n<\/li>\n<li>\n<p>\u9759\u6001\u914d\u7f6e&#xff1a;Netlink\u547d\u4ee4\u624b\u52a8\u6dfb\u52a0<\/p>\n<p>ip mroute add 192.168.1.100 224.0.0.1 dev eth0\n<\/li>\n<li>\n<p>\u8d85\u65f6\u6e05\u7406&#xff1a;10\u79d2\u672a\u89e3\u6790\u81ea\u52a8\u4e22\u5f03&#xff08;\u53ef\u914d\u7f6e&#xff09;<\/p>\n<p>#define MFC_UNRES_TIMEOUT (10*HZ)\n<\/li>\n<\/ul>\n<h5>2. \u7528\u6237\u6001\u534f\u540c\u673a\u5236<\/h5>\n<p>\u5185\u6838\u901a\u8fc7\u539f\u59cb\u5957\u63a5\u5b57\u4e0emrouted\/pimd\u4ea4\u4e92&#xff1a;<\/p>\n<p>static void igmpmsg_netlink_event(struct mr_table *mrt, struct sk_buff *pkt)<br \/>\n{<br \/>\n    \/\/ \u6784\u9020IGMPMSG_NOCACHE\u6d88\u606f<br \/>\n    \/\/ \u901a\u8fc7NETLINK\u53d1\u9001\u5230\u7528\u6237\u6001<br \/>\n}<\/p>\n<p>\u5b9e\u73b0\u5b9e\u65f6\u8def\u7531\u66f4\u65b0\u548c\u672a\u89e3\u6790\u5305\u901a\u77e5\u3002<\/p>\n<hr \/>\n<h4>\u56db\u3001\u534f\u8bae\u652f\u6301&#xff1a;\u7075\u6d3b\u7684\u5904\u7406\u6846\u67b6<\/h4>\n<h5>1. PIM\u534f\u8bae\u5904\u7406<\/h5>\n<p>\/\/ PIMv2\u5904\u7406\u51fd\u6570<br \/>\nstatic int pim_rcv(struct sk_buff *skb)<br \/>\n{<br \/>\n    if (pim-&gt;type !&#061; ((PIM_VERSION&lt;&lt;4)|PIM_TYPE_REGISTER))<br \/>\n        goto drop;<br \/>\n    \/\/ \u9a8c\u8bc1\u6821\u9a8c\u548c<br \/>\n    \/\/ \u8f6c\u53d1\u5230\u6ce8\u518c\u63a5\u53e3<br \/>\n}<\/p>\n<p>\u652f\u6301PIM-SM\/PIM-DM\u5173\u952e\u7279\u6027&#xff0c;\u5305\u62ec\u6ce8\u518c\u505c\u6b62\u673a\u5236\u3002<\/p>\n<h5>2. IGMP\u4ee3\u7406<\/h5>\n<p>static int ipmr_cache_report(struct mr_table *mrt,<br \/>\n                struct sk_buff *pkt, vifi_t vifi, int assert)<br \/>\n{<br \/>\n    \/\/ \u6784\u9020IGMPMSG_WHOLEPKT\u6d88\u606f<br \/>\n    \/\/ \u53d1\u9001\u5230\u7528\u6237\u6001\u5957\u63a5\u5b57<br \/>\n}<\/p>\n<p>\u5b9e\u73b0\u7ec4\u64ad\u7ec4\u6210\u5458\u5173\u7cfb\u7684\u8de8\u7f51\u7edc\u4f20\u64ad\u3002<\/p>\n<hr \/>\n<h4>\u4e94\u3001\u521b\u65b0\u8bbe\u8ba1&#xff1a;\u6027\u80fd\u4e0e\u6269\u5c55\u6027<\/h4>\n<li>\n<p>\u591a\u8868\u652f\u6301&#xff08;CONFIG_IP_MROUTE_MULTIPLE_TABLES&#xff09;<\/p>\n<p>struct mr_table *ipmr_new_table(struct net *net, u32 id)<br \/>\n{<br \/>\n    \/\/ \u521b\u5efa\u72ec\u7acb\u8def\u7531\u8868<br \/>\n}<\/p>\n<p>\u652f\u6301VRF\u573a\u666f&#xff0c;\u5b9e\u73b0\u7f51\u7edc\u9694\u79bb\u3002<\/p>\n<\/li>\n<li>\n<p>RCU&#043;\u54c8\u5e0c\u8868\u68c0\u7d22<\/p>\n<p>struct mfc_cache *ipmr_cache_find(struct mr_table *mrt,<br \/>\n                 __be32 origin, __be32 mcastgrp)<br \/>\n{<br \/>\n    \/\/ RCU\u4fdd\u62a4\u7684\u54c8\u5e0c\u67e5\u627e<br \/>\n}<\/p>\n<p>\u5b9e\u73b0\u767e\u4e07\u7ea7\u8def\u7531\u6761\u76ee\u4e0b\u7684\u5fae\u79d2\u7ea7\u68c0\u7d22\u3002<\/p>\n<\/li>\n<li>\n<p>\u52a8\u6001\u5b9a\u65f6\u5668\u7ba1\u7406<br \/>\n\u4ec5\u5f53\u5b58\u5728\u672a\u89e3\u6790\u6761\u76ee\u65f6\u6fc0\u6d3b\u5b9a\u65f6\u5668&#xff0c;\u51cf\u5c11\u7a7a\u8f6c\u5f00\u9500\u3002<\/p>\n<\/li>\n<hr \/>\n<h4>\u516d\u3001\u5b9e\u6218\u6d1e\u5bdf&#xff1a;\u5b9a\u4f4d\u591a\u64ad\u95ee\u9898\u7684\u5173\u952e\u70b9<\/h4>\n<li>\n<p>\u67e5\u770b\u8def\u7531\u8868<\/p>\n<p>cat \/proc\/net\/ip_mr_cache<br \/>\n# \u8f93\u51fa\u793a\u4f8b&#xff1a;<br \/>\n# Group    Origin   Iif Pkts Bytes Wrong Oifs<br \/>\n# E0000001 C0A80101 2   104  8576  0     1:3<\/p>\n<p>\u5173\u6ce8&#034;Wrong Oifs&#034;\u8ba1\u6570&#xff0c;\u9ad8\u6570\u503c\u8868\u660e\u63a5\u53e3\u914d\u7f6e\u9519\u8bef\u3002<\/p>\n<\/li>\n<li>\n<p>\u76d1\u63a7\u865a\u62df\u63a5\u53e3<\/p>\n<p>cat \/proc\/net\/ip_mr_vif<br \/>\n# \u8f93\u51fa\u793a\u4f8b&#xff1a;<br \/>\n# Interface BytesIn PktsIn BytesOut PktsOut Flags<br \/>\n# vif0      15MB    1200   12MB    900     VIFF_TUNNEL<\/p>\n<p>\u5f02\u5e38\u5b57\u8282\u6570\u6307\u793a\u8def\u7531\u73af\u8def\u6216\u914d\u7f6e\u9519\u8bef\u3002<\/p>\n<\/li>\n<li>\n<p>\u52a8\u6001\u8c03\u8bd5<br \/>\n\u542f\u7528\u5185\u6838\u8c03\u8bd5\u9009\u9879&#xff1a;<\/p>\n<p>echo 1 &gt; \/proc\/sys\/net\/ipv4\/route\/mcast_debug\n<\/li>\n<hr \/>\n<h4>\u7ed3\u8bed<\/h4>\n<p>Linux\u7684IPv4\u591a\u64ad\u8def\u7531\u901a\u8fc7\u5206\u5c42\u67b6\u6784\u548c\u52a8\u6001\u7ba1\u7406\u673a\u5236&#xff0c;\u5728\u590d\u6742\u7f51\u7edc\u73af\u5883\u4e2d\u5b9e\u73b0\u4e86\u9ad8\u6548\u7a33\u5b9a\u7684\u6570\u636e\u5206\u53d1\u3002\u5176\u6838\u5fc3\u8bbe\u8ba1\u601d\u60f3\u5305\u62ec&#xff1a;<\/p>\n<li>\n<p>\u63a7\u5236\u4e0e\u8f6c\u53d1\u5206\u79bb&#xff1a;\u7528\u6237\u6001\u534f\u8bae\u5904\u7406&#043;\u5185\u6838\u6001\u5feb\u901f\u8f6c\u53d1<\/p>\n<\/li>\n<li>\n<p>\u65f6\u95f4\u7a7a\u95f4\u5e73\u8861&#xff1a;\u54c8\u5e0c\u8868&#043;\u94fe\u8868\u7684\u6df7\u5408\u5b58\u50a8\u7ed3\u6784<\/p>\n<\/li>\n<li>\n<p>\u60f0\u6027\u8ba1\u7b97&#xff1a;\u6309\u9700\u6fc0\u6d3b\u5904\u7406\u6d41\u7a0b<\/p>\n<\/li>\n<p>\u8fd9\u4e9b\u8bbe\u8ba1\u4f7f\u5f97Linux\u6210\u4e3a\u4ece\u6570\u636e\u4e2d\u5fc3\u5230\u7535\u4fe1\u7f51\u7edc\u7684\u591a\u64ad\u89e3\u51b3\u65b9\u6848\u57fa\u77f3&#xff0c;\u4e3a5G\u7f51\u7edc\u3001IPTV\u7b49\u573a\u666f\u63d0\u4f9b\u4e86\u5173\u952e\u57fa\u7840\u8bbe\u65bd\u652f\u6301\u3002<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" alt=\"\" height=\"1024\" src=\"https:\/\/www.wsisp.com\/helps\/wp-content\/uploads\/2025\/07\/20250729205732-688935bcddc50.png\" width=\"1024\" \/><\/p>\n<p>\/*<br \/>\n *IP multicast routing support for mrouted 3.6\/3.8<br \/>\n *<br \/>\n *(c) 1995 Alan Cox, &lt;alan&#064;lxorguk.ukuu.org.uk&gt;<br \/>\n *  Linux Consultancy and Custom Driver Development<br \/>\n *<br \/>\n *This program is free software; you can redistribute it and\/or<br \/>\n *modify it under the terms of the GNU General Public License<br \/>\n *as published by the Free Software Foundation; either version<br \/>\n *2 of the License, or (at your option) any later version.<br \/>\n *<br \/>\n *Fixes:<br \/>\n *Michael Chastain:Incorrect size of copying.<br \/>\n *Alan Cox:Added the cache manager code<br \/>\n *Alan Cox:Fixed the clone\/copy bug and device race.<br \/>\n *Mike McLagan:Routing by source<br \/>\n *Malcolm Beattie:Buffer handling fixes.<br \/>\n *Alexey Kuznetsov:Double buffer free and other fixes.<br \/>\n *SVR Anand:Fixed several multicast bugs and problems.<br \/>\n *Alexey Kuznetsov:Status, optimisations and more.<br \/>\n *Brad Parker:Better behaviour on mrouted upcall<br \/>\n *overflow.<br \/>\n *      Carlos Picoto           :       PIMv1 Support<br \/>\n *Pavlin Ivanov Radoslavov:PIMv2 Registers must checksum only PIM header<br \/>\n *Relax this requirement to work with older peers.<br \/>\n *<br \/>\n *\/<\/p>\n<p>#include &lt;linux\/uaccess.h&gt;<br \/>\n#include &lt;linux\/types.h&gt;<br \/>\n#include &lt;linux\/cache.h&gt;<br \/>\n#include &lt;linux\/capability.h&gt;<br \/>\n#include &lt;linux\/errno.h&gt;<br \/>\n#include &lt;linux\/mm.h&gt;<br \/>\n#include &lt;linux\/kernel.h&gt;<br \/>\n#include &lt;linux\/fcntl.h&gt;<br \/>\n#include &lt;linux\/stat.h&gt;<br \/>\n#include &lt;linux\/socket.h&gt;<br \/>\n#include &lt;linux\/in.h&gt;<br \/>\n#include &lt;linux\/inet.h&gt;<br \/>\n#include &lt;linux\/netdevice.h&gt;<br \/>\n#include &lt;linux\/inetdevice.h&gt;<br \/>\n#include &lt;linux\/igmp.h&gt;<br \/>\n#include &lt;linux\/proc_fs.h&gt;<br \/>\n#include &lt;linux\/seq_file.h&gt;<br \/>\n#include &lt;linux\/mroute.h&gt;<br \/>\n#include &lt;linux\/init.h&gt;<br \/>\n#include &lt;linux\/if_ether.h&gt;<br \/>\n#include &lt;linux\/slab.h&gt;<br \/>\n#include &lt;net\/net_namespace.h&gt;<br \/>\n#include &lt;net\/ip.h&gt;<br \/>\n#include &lt;net\/protocol.h&gt;<br \/>\n#include &lt;linux\/skbuff.h&gt;<br \/>\n#include &lt;net\/route.h&gt;<br \/>\n#include &lt;net\/icmp.h&gt;<br \/>\n#include &lt;net\/udp.h&gt;<br \/>\n#include &lt;net\/raw.h&gt;<br \/>\n#include &lt;linux\/notifier.h&gt;<br \/>\n#include &lt;linux\/if_arp.h&gt;<br \/>\n#include &lt;linux\/netfilter_ipv4.h&gt;<br \/>\n#include &lt;linux\/compat.h&gt;<br \/>\n#include &lt;linux\/export.h&gt;<br \/>\n#include &lt;linux\/rhashtable.h&gt;<br \/>\n#include &lt;net\/ip_tunnels.h&gt;<br \/>\n#include &lt;net\/checksum.h&gt;<br \/>\n#include &lt;net\/netlink.h&gt;<br \/>\n#include &lt;net\/fib_rules.h&gt;<br \/>\n#include &lt;linux\/netconf.h&gt;<br \/>\n#include &lt;net\/nexthop.h&gt;<br \/>\n#include &lt;net\/switchdev.h&gt;<\/p>\n<p>#include &lt;linux\/nospec.h&gt;<\/p>\n<p>struct ipmr_rule {<br \/>\nstruct fib_rulecommon;<br \/>\n};<\/p>\n<p>struct ipmr_result {<br \/>\nstruct mr_table*mrt;<br \/>\n};<\/p>\n<p>\/* Big lock, protecting vif table, mrt cache and mroute socket state.<br \/>\n * Note that the changes are semaphored via rtnl_lock.<br \/>\n *\/<\/p>\n<p>static DEFINE_RWLOCK(mrt_lock);<\/p>\n<p>\/* Multicast router control variables *\/<\/p>\n<p>\/* Special spinlock for queue of unresolved entries *\/<br \/>\nstatic DEFINE_SPINLOCK(mfc_unres_lock);<\/p>\n<p>\/* We return to original Alan&#039;s scheme. Hash table of resolved<br \/>\n * entries is changed only in process context and protected<br \/>\n * with weak lock mrt_lock. Queue of unresolved entries is protected<br \/>\n * with strong spinlock mfc_unres_lock.<br \/>\n *<br \/>\n * In this case data path is free of exclusive locks at all.<br \/>\n *\/<\/p>\n<p>static struct kmem_cache *mrt_cachep __ro_after_init;<\/p>\n<p>static struct mr_table *ipmr_new_table(struct net *net, u32 id);<br \/>\nstatic void ipmr_free_table(struct mr_table *mrt);<\/p>\n<p>static void ip_mr_forward(struct net *net, struct mr_table *mrt,<br \/>\n  struct net_device *dev, struct sk_buff *skb,<br \/>\n  struct mfc_cache *cache, int local);<br \/>\nstatic int ipmr_cache_report(struct mr_table *mrt,<br \/>\n     struct sk_buff *pkt, vifi_t vifi, int assert);<br \/>\nstatic void mroute_netlink_event(struct mr_table *mrt, struct mfc_cache *mfc,<br \/>\n int cmd);<br \/>\nstatic void igmpmsg_netlink_event(struct mr_table *mrt, struct sk_buff *pkt);<br \/>\nstatic void mroute_clean_tables(struct mr_table *mrt, bool all);<br \/>\nstatic void ipmr_expire_process(struct timer_list *t);<\/p>\n<p>#ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES<br \/>\n#define ipmr_for_each_table(mrt, net) \\\\<br \/>\nlist_for_each_entry_rcu(mrt, &amp;net-&gt;ipv4.mr_tables, list)<\/p>\n<p>static struct mr_table *ipmr_mr_table_iter(struct net *net,<br \/>\n   struct mr_table *mrt)<br \/>\n{<br \/>\nstruct mr_table *ret;<\/p>\n<p>if (!mrt)<br \/>\nret &#061; list_entry_rcu(net-&gt;ipv4.mr_tables.next,<br \/>\n     struct mr_table, list);<br \/>\nelse<br \/>\nret &#061; list_entry_rcu(mrt-&gt;list.next,<br \/>\n     struct mr_table, list);<\/p>\n<p>if (&amp;ret-&gt;list &#061;&#061; &amp;net-&gt;ipv4.mr_tables)<br \/>\nreturn NULL;<br \/>\nreturn ret;<br \/>\n}<\/p>\n<p>static struct mr_table *ipmr_get_table(struct net *net, u32 id)<br \/>\n{<br \/>\nstruct mr_table *mrt;<\/p>\n<p>ipmr_for_each_table(mrt, net) {<br \/>\nif (mrt-&gt;id &#061;&#061; id)<br \/>\nreturn mrt;<br \/>\n}<br \/>\nreturn NULL;<br \/>\n}<\/p>\n<p>static int ipmr_fib_lookup(struct net *net, struct flowi4 *flp4,<br \/>\n   struct mr_table **mrt)<br \/>\n{<br \/>\nint err;<br \/>\nstruct ipmr_result res;<br \/>\nstruct fib_lookup_arg arg &#061; {<br \/>\n.result &#061; &amp;res,<br \/>\n.flags &#061; FIB_LOOKUP_NOREF,<br \/>\n};<\/p>\n<p>\/* update flow if oif or iif point to device enslaved to l3mdev *\/<br \/>\nl3mdev_update_flow(net, flowi4_to_flowi(flp4));<\/p>\n<p>err &#061; fib_rules_lookup(net-&gt;ipv4.mr_rules_ops,<br \/>\n       flowi4_to_flowi(flp4), 0, &amp;arg);<br \/>\nif (err &lt; 0)<br \/>\nreturn err;<br \/>\n*mrt &#061; res.mrt;<br \/>\nreturn 0;<br \/>\n}<\/p>\n<p>static int ipmr_rule_action(struct fib_rule *rule, struct flowi *flp,<br \/>\n    int flags, struct fib_lookup_arg *arg)<br \/>\n{<br \/>\nstruct ipmr_result *res &#061; arg-&gt;result;<br \/>\nstruct mr_table *mrt;<\/p>\n<p>switch (rule-&gt;action) {<br \/>\ncase FR_ACT_TO_TBL:<br \/>\nbreak;<br \/>\ncase FR_ACT_UNREACHABLE:<br \/>\nreturn -ENETUNREACH;<br \/>\ncase FR_ACT_PROHIBIT:<br \/>\nreturn -EACCES;<br \/>\ncase FR_ACT_BLACKHOLE:<br \/>\ndefault:<br \/>\nreturn -EINVAL;<br \/>\n}<\/p>\n<p>arg-&gt;table &#061; fib_rule_get_table(rule, arg);<\/p>\n<p>mrt &#061; ipmr_get_table(rule-&gt;fr_net, arg-&gt;table);<br \/>\nif (!mrt)<br \/>\nreturn -EAGAIN;<br \/>\nres-&gt;mrt &#061; mrt;<br \/>\nreturn 0;<br \/>\n}<\/p>\n<p>static int ipmr_rule_match(struct fib_rule *rule, struct flowi *fl, int flags)<br \/>\n{<br \/>\nreturn 1;<br \/>\n}<\/p>\n<p>static const struct nla_policy ipmr_rule_policy[FRA_MAX &#043; 1] &#061; {<br \/>\nFRA_GENERIC_POLICY,<br \/>\n};<\/p>\n<p>static int ipmr_rule_configure(struct fib_rule *rule, struct sk_buff *skb,<br \/>\n       struct fib_rule_hdr *frh, struct nlattr **tb,<br \/>\n       struct netlink_ext_ack *extack)<br \/>\n{<br \/>\nreturn 0;<br \/>\n}<\/p>\n<p>static int ipmr_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh,<br \/>\n     struct nlattr **tb)<br \/>\n{<br \/>\nreturn 1;<br \/>\n}<\/p>\n<p>static int ipmr_rule_fill(struct fib_rule *rule, struct sk_buff *skb,<br \/>\n  struct fib_rule_hdr *frh)<br \/>\n{<br \/>\nfrh-&gt;dst_len &#061; 0;<br \/>\nfrh-&gt;src_len &#061; 0;<br \/>\nfrh-&gt;tos     &#061; 0;<br \/>\nreturn 0;<br \/>\n}<\/p>\n<p>static const struct fib_rules_ops __net_initconst ipmr_rules_ops_template &#061; {<br \/>\n.family&#061; RTNL_FAMILY_IPMR,<br \/>\n.rule_size&#061; sizeof(struct ipmr_rule),<br \/>\n.addr_size&#061; sizeof(u32),<br \/>\n.action&#061; ipmr_rule_action,<br \/>\n.match&#061; ipmr_rule_match,<br \/>\n.configure&#061; ipmr_rule_configure,<br \/>\n.compare&#061; ipmr_rule_compare,<br \/>\n.fill&#061; ipmr_rule_fill,<br \/>\n.nlgroup&#061; RTNLGRP_IPV4_RULE,<br \/>\n.policy&#061; ipmr_rule_policy,<br \/>\n.owner&#061; THIS_MODULE,<br \/>\n};<\/p>\n<p>static int __net_init ipmr_rules_init(struct net *net)<br \/>\n{<br \/>\nstruct fib_rules_ops *ops;<br \/>\nstruct mr_table *mrt;<br \/>\nint err;<\/p>\n<p>ops &#061; fib_rules_register(&amp;ipmr_rules_ops_template, net);<br \/>\nif (IS_ERR(ops))<br \/>\nreturn PTR_ERR(ops);<\/p>\n<p>INIT_LIST_HEAD(&amp;net-&gt;ipv4.mr_tables);<\/p>\n<p>mrt &#061; ipmr_new_table(net, RT_TABLE_DEFAULT);<br \/>\nif (IS_ERR(mrt)) {<br \/>\nerr &#061; PTR_ERR(mrt);<br \/>\ngoto err1;<br \/>\n}<\/p>\n<p>err &#061; fib_default_rule_add(ops, 0x7fff, RT_TABLE_DEFAULT, 0);<br \/>\nif (err &lt; 0)<br \/>\ngoto err2;<\/p>\n<p>net-&gt;ipv4.mr_rules_ops &#061; ops;<br \/>\nreturn 0;<\/p>\n<p>err2:<br \/>\nipmr_free_table(mrt);<br \/>\nerr1:<br \/>\nfib_rules_unregister(ops);<br \/>\nreturn err;<br \/>\n}<\/p>\n<p>static void __net_exit ipmr_rules_exit(struct net *net)<br \/>\n{<br \/>\nstruct mr_table *mrt, *next;<\/p>\n<p>rtnl_lock();<br \/>\nlist_for_each_entry_safe(mrt, next, &amp;net-&gt;ipv4.mr_tables, list) {<br \/>\nlist_del(&amp;mrt-&gt;list);<br \/>\nipmr_free_table(mrt);<br \/>\n}<br \/>\nfib_rules_unregister(net-&gt;ipv4.mr_rules_ops);<br \/>\nrtnl_unlock();<br \/>\n}<\/p>\n<p>static int ipmr_rules_dump(struct net *net, struct notifier_block *nb)<br \/>\n{<br \/>\nreturn fib_rules_dump(net, nb, RTNL_FAMILY_IPMR);<br \/>\n}<\/p>\n<p>static unsigned int ipmr_rules_seq_read(struct net *net)<br \/>\n{<br \/>\nreturn fib_rules_seq_read(net, RTNL_FAMILY_IPMR);<br \/>\n}<\/p>\n<p>bool ipmr_rule_default(const struct fib_rule *rule)<br \/>\n{<br \/>\nreturn fib_rule_matchall(rule) &amp;&amp; rule-&gt;table &#061;&#061; RT_TABLE_DEFAULT;<br \/>\n}<br \/>\nEXPORT_SYMBOL(ipmr_rule_default);<br \/>\n#else<br \/>\n#define ipmr_for_each_table(mrt, net) \\\\<br \/>\nfor (mrt &#061; net-&gt;ipv4.mrt; mrt; mrt &#061; NULL)<\/p>\n<p>static struct mr_table *ipmr_mr_table_iter(struct net *net,<br \/>\n   struct mr_table *mrt)<br \/>\n{<br \/>\nif (!mrt)<br \/>\nreturn net-&gt;ipv4.mrt;<br \/>\nreturn NULL;<br \/>\n}<\/p>\n<p>static struct mr_table *ipmr_get_table(struct net *net, u32 id)<br \/>\n{<br \/>\nreturn net-&gt;ipv4.mrt;<br \/>\n}<\/p>\n<p>static int ipmr_fib_lookup(struct net *net, struct flowi4 *flp4,<br \/>\n   struct mr_table **mrt)<br \/>\n{<br \/>\n*mrt &#061; net-&gt;ipv4.mrt;<br \/>\nreturn 0;<br \/>\n}<\/p>\n<p>static int __net_init ipmr_rules_init(struct net *net)<br \/>\n{<br \/>\nstruct mr_table *mrt;<\/p>\n<p>mrt &#061; ipmr_new_table(net, RT_TABLE_DEFAULT);<br \/>\nif (IS_ERR(mrt))<br \/>\nreturn PTR_ERR(mrt);<br \/>\nnet-&gt;ipv4.mrt &#061; mrt;<br \/>\nreturn 0;<br \/>\n}<\/p>\n<p>static void __net_exit ipmr_rules_exit(struct net *net)<br \/>\n{<br \/>\nrtnl_lock();<br \/>\nipmr_free_table(net-&gt;ipv4.mrt);<br \/>\nnet-&gt;ipv4.mrt &#061; NULL;<br \/>\nrtnl_unlock();<br \/>\n}<\/p>\n<p>static int ipmr_rules_dump(struct net *net, struct notifier_block *nb)<br \/>\n{<br \/>\nreturn 0;<br \/>\n}<\/p>\n<p>static unsigned int ipmr_rules_seq_read(struct net *net)<br \/>\n{<br \/>\nreturn 0;<br \/>\n}<\/p>\n<p>bool ipmr_rule_default(const struct fib_rule *rule)<br \/>\n{<br \/>\nreturn true;<br \/>\n}<br \/>\nEXPORT_SYMBOL(ipmr_rule_default);<br \/>\n#endif<\/p>\n<p>static inline int ipmr_hash_cmp(struct rhashtable_compare_arg *arg,<br \/>\nconst void *ptr)<br \/>\n{<br \/>\nconst struct mfc_cache_cmp_arg *cmparg &#061; arg-&gt;key;<br \/>\nstruct mfc_cache *c &#061; (struct mfc_cache *)ptr;<\/p>\n<p>return cmparg-&gt;mfc_mcastgrp !&#061; c-&gt;mfc_mcastgrp ||<br \/>\n       cmparg-&gt;mfc_origin !&#061; c-&gt;mfc_origin;<br \/>\n}<\/p>\n<p>static const struct rhashtable_params ipmr_rht_params &#061; {<br \/>\n.head_offset &#061; offsetof(struct mr_mfc, mnode),<br \/>\n.key_offset &#061; offsetof(struct mfc_cache, cmparg),<br \/>\n.key_len &#061; sizeof(struct mfc_cache_cmp_arg),<br \/>\n.nelem_hint &#061; 3,<br \/>\n.locks_mul &#061; 1,<br \/>\n.obj_cmpfn &#061; ipmr_hash_cmp,<br \/>\n.automatic_shrinking &#061; true,<br \/>\n};<\/p>\n<p>static void ipmr_new_table_set(struct mr_table *mrt,<br \/>\n       struct net *net)<br \/>\n{<br \/>\n#ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES<br \/>\nlist_add_tail_rcu(&amp;mrt-&gt;list, &amp;net-&gt;ipv4.mr_tables);<br \/>\n#endif<br \/>\n}<\/p>\n<p>static struct mfc_cache_cmp_arg ipmr_mr_table_ops_cmparg_any &#061; {<br \/>\n.mfc_mcastgrp &#061; htonl(INADDR_ANY),<br \/>\n.mfc_origin &#061; htonl(INADDR_ANY),<br \/>\n};<\/p>\n<p>static struct mr_table_ops ipmr_mr_table_ops &#061; {<br \/>\n.rht_params &#061; &amp;ipmr_rht_params,<br \/>\n.cmparg_any &#061; &amp;ipmr_mr_table_ops_cmparg_any,<br \/>\n};<\/p>\n<p>static struct mr_table *ipmr_new_table(struct net *net, u32 id)<br \/>\n{<br \/>\nstruct mr_table *mrt;<\/p>\n<p>\/* &#034;pimreg%u&#034; should not exceed 16 bytes (IFNAMSIZ) *\/<br \/>\nif (id !&#061; RT_TABLE_DEFAULT &amp;&amp; id &gt;&#061; 1000000000)<br \/>\nreturn ERR_PTR(-EINVAL);<\/p>\n<p>mrt &#061; ipmr_get_table(net, id);<br \/>\nif (mrt)<br \/>\nreturn mrt;<\/p>\n<p>return mr_table_alloc(net, id, &amp;ipmr_mr_table_ops,<br \/>\n      ipmr_expire_process, ipmr_new_table_set);<br \/>\n}<\/p>\n<p>static void ipmr_free_table(struct mr_table *mrt)<br \/>\n{<br \/>\ndel_timer_sync(&amp;mrt-&gt;ipmr_expire_timer);<br \/>\nmroute_clean_tables(mrt, true);<br \/>\nrhltable_destroy(&amp;mrt-&gt;mfc_hash);<br \/>\nkfree(mrt);<br \/>\n}<\/p>\n<p>\/* Service routines creating virtual interfaces: DVMRP tunnels and PIMREG *\/<\/p>\n<p>static void ipmr_del_tunnel(struct net_device *dev, struct vifctl *v)<br \/>\n{<br \/>\nstruct net *net &#061; dev_net(dev);<\/p>\n<p>dev_close(dev);<\/p>\n<p>dev &#061; __dev_get_by_name(net, &#034;tunl0&#034;);<br \/>\nif (dev) {<br \/>\nconst struct net_device_ops *ops &#061; dev-&gt;netdev_ops;<br \/>\nstruct ifreq ifr;<br \/>\nstruct ip_tunnel_parm p;<\/p>\n<p>memset(&amp;p, 0, sizeof(p));<br \/>\np.iph.daddr &#061; v-&gt;vifc_rmt_addr.s_addr;<br \/>\np.iph.saddr &#061; v-&gt;vifc_lcl_addr.s_addr;<br \/>\np.iph.version &#061; 4;<br \/>\np.iph.ihl &#061; 5;<br \/>\np.iph.protocol &#061; IPPROTO_IPIP;<br \/>\nsprintf(p.name, &#034;dvmrp%d&#034;, v-&gt;vifc_vifi);<br \/>\nifr.ifr_ifru.ifru_data &#061; (__force void __user *)&amp;p;<\/p>\n<p>if (ops-&gt;ndo_do_ioctl) {<br \/>\nmm_segment_t oldfs &#061; get_fs();<\/p>\n<p>set_fs(KERNEL_DS);<br \/>\nops-&gt;ndo_do_ioctl(dev, &amp;ifr, SIOCDELTUNNEL);<br \/>\nset_fs(oldfs);<br \/>\n}<br \/>\n}<br \/>\n}<\/p>\n<p>\/* Initialize ipmr pimreg\/tunnel in_device *\/<br \/>\nstatic bool ipmr_init_vif_indev(const struct net_device *dev)<br \/>\n{<br \/>\nstruct in_device *in_dev;<\/p>\n<p>ASSERT_RTNL();<\/p>\n<p>in_dev &#061; __in_dev_get_rtnl(dev);<br \/>\nif (!in_dev)<br \/>\nreturn false;<br \/>\nipv4_devconf_setall(in_dev);<br \/>\nneigh_parms_data_state_setall(in_dev-&gt;arp_parms);<br \/>\nIPV4_DEVCONF(in_dev-&gt;cnf, RP_FILTER) &#061; 0;<\/p>\n<p>return true;<br \/>\n}<\/p>\n<p>static struct net_device *ipmr_new_tunnel(struct net *net, struct vifctl *v)<br \/>\n{<br \/>\nstruct net_device  *dev;<\/p>\n<p>dev &#061; __dev_get_by_name(net, &#034;tunl0&#034;);<\/p>\n<p>if (dev) {<br \/>\nconst struct net_device_ops *ops &#061; dev-&gt;netdev_ops;<br \/>\nint err;<br \/>\nstruct ifreq ifr;<br \/>\nstruct ip_tunnel_parm p;<\/p>\n<p>memset(&amp;p, 0, sizeof(p));<br \/>\np.iph.daddr &#061; v-&gt;vifc_rmt_addr.s_addr;<br \/>\np.iph.saddr &#061; v-&gt;vifc_lcl_addr.s_addr;<br \/>\np.iph.version &#061; 4;<br \/>\np.iph.ihl &#061; 5;<br \/>\np.iph.protocol &#061; IPPROTO_IPIP;<br \/>\nsprintf(p.name, &#034;dvmrp%d&#034;, v-&gt;vifc_vifi);<br \/>\nifr.ifr_ifru.ifru_data &#061; (__force void __user *)&amp;p;<\/p>\n<p>if (ops-&gt;ndo_do_ioctl) {<br \/>\nmm_segment_t oldfs &#061; get_fs();<\/p>\n<p>set_fs(KERNEL_DS);<br \/>\nerr &#061; ops-&gt;ndo_do_ioctl(dev, &amp;ifr, SIOCADDTUNNEL);<br \/>\nset_fs(oldfs);<br \/>\n} else {<br \/>\nerr &#061; -EOPNOTSUPP;<br \/>\n}<br \/>\ndev &#061; NULL;<\/p>\n<p>if (err &#061;&#061; 0 &amp;&amp;<br \/>\n    (dev &#061; __dev_get_by_name(net, p.name)) !&#061; NULL) {<br \/>\ndev-&gt;flags |&#061; IFF_MULTICAST;<br \/>\nif (!ipmr_init_vif_indev(dev))<br \/>\ngoto failure;<br \/>\nif (dev_open(dev))<br \/>\ngoto failure;<br \/>\ndev_hold(dev);<br \/>\n}<br \/>\n}<br \/>\nreturn dev;<\/p>\n<p>failure:<br \/>\nunregister_netdevice(dev);<br \/>\nreturn NULL;<br \/>\n}<\/p>\n<p>#if defined(CONFIG_IP_PIMSM_V1) || defined(CONFIG_IP_PIMSM_V2)<br \/>\nstatic netdev_tx_t reg_vif_xmit(struct sk_buff *skb, struct net_device *dev)<br \/>\n{<br \/>\nstruct net *net &#061; dev_net(dev);<br \/>\nstruct mr_table *mrt;<br \/>\nstruct flowi4 fl4 &#061; {<br \/>\n.flowi4_oif&#061; dev-&gt;ifindex,<br \/>\n.flowi4_iif&#061; skb-&gt;skb_iif ? : LOOPBACK_IFINDEX,<br \/>\n.flowi4_mark&#061; skb-&gt;mark,<br \/>\n};<br \/>\nint err;<\/p>\n<p>err &#061; ipmr_fib_lookup(net, &amp;fl4, &amp;mrt);<br \/>\nif (err &lt; 0) {<br \/>\nkfree_skb(skb);<br \/>\nreturn err;<br \/>\n}<\/p>\n<p>read_lock(&amp;mrt_lock);<br \/>\ndev-&gt;stats.tx_bytes &#043;&#061; skb-&gt;len;<br \/>\ndev-&gt;stats.tx_packets&#043;&#043;;<br \/>\nipmr_cache_report(mrt, skb, mrt-&gt;mroute_reg_vif_num, IGMPMSG_WHOLEPKT);<br \/>\nread_unlock(&amp;mrt_lock);<br \/>\nkfree_skb(skb);<br \/>\nreturn NETDEV_TX_OK;<br \/>\n}<\/p>\n<p>static int reg_vif_get_iflink(const struct net_device *dev)<br \/>\n{<br \/>\nreturn 0;<br \/>\n}<\/p>\n<p>static const struct net_device_ops reg_vif_netdev_ops &#061; {<br \/>\n.ndo_start_xmit&#061; reg_vif_xmit,<br \/>\n.ndo_get_iflink &#061; reg_vif_get_iflink,<br \/>\n};<\/p>\n<p>static void reg_vif_setup(struct net_device *dev)<br \/>\n{<br \/>\ndev-&gt;type&#061; ARPHRD_PIMREG;<br \/>\ndev-&gt;mtu&#061; ETH_DATA_LEN &#8211; sizeof(struct iphdr) &#8211; 8;<br \/>\ndev-&gt;flags&#061; IFF_NOARP;<br \/>\ndev-&gt;netdev_ops&#061; &amp;reg_vif_netdev_ops;<br \/>\ndev-&gt;needs_free_netdev&#061; true;<br \/>\ndev-&gt;features|&#061; NETIF_F_NETNS_LOCAL;<br \/>\n}<\/p>\n<p>static struct net_device *ipmr_reg_vif(struct net *net, struct mr_table *mrt)<br \/>\n{<br \/>\nstruct net_device *dev;<br \/>\nchar name[IFNAMSIZ];<\/p>\n<p>if (mrt-&gt;id &#061;&#061; RT_TABLE_DEFAULT)<br \/>\nsprintf(name, &#034;pimreg&#034;);<br \/>\nelse<br \/>\nsprintf(name, &#034;pimreg%u&#034;, mrt-&gt;id);<\/p>\n<p>dev &#061; alloc_netdev(0, name, NET_NAME_UNKNOWN, reg_vif_setup);<\/p>\n<p>if (!dev)<br \/>\nreturn NULL;<\/p>\n<p>dev_net_set(dev, net);<\/p>\n<p>if (register_netdevice(dev)) {<br \/>\nfree_netdev(dev);<br \/>\nreturn NULL;<br \/>\n}<\/p>\n<p>if (!ipmr_init_vif_indev(dev))<br \/>\ngoto failure;<br \/>\nif (dev_open(dev))<br \/>\ngoto failure;<\/p>\n<p>dev_hold(dev);<\/p>\n<p>return dev;<\/p>\n<p>failure:<br \/>\nunregister_netdevice(dev);<br \/>\nreturn NULL;<br \/>\n}<\/p>\n<p>\/* called with rcu_read_lock() *\/<br \/>\nstatic int __pim_rcv(struct mr_table *mrt, struct sk_buff *skb,<br \/>\n     unsigned int pimlen)<br \/>\n{<br \/>\nstruct net_device *reg_dev &#061; NULL;<br \/>\nstruct iphdr *encap;<\/p>\n<p>encap &#061; (struct iphdr *)(skb_transport_header(skb) &#043; pimlen);<br \/>\n\/* Check that:<br \/>\n * a. packet is really sent to a multicast group<br \/>\n * b. packet is not a NULL-REGISTER<br \/>\n * c. packet is not truncated<br \/>\n *\/<br \/>\nif (!ipv4_is_multicast(encap-&gt;daddr) ||<br \/>\n    encap-&gt;tot_len &#061;&#061; 0 ||<br \/>\n    ntohs(encap-&gt;tot_len) &#043; pimlen &gt; skb-&gt;len)<br \/>\nreturn 1;<\/p>\n<p>read_lock(&amp;mrt_lock);<br \/>\nif (mrt-&gt;mroute_reg_vif_num &gt;&#061; 0)<br \/>\nreg_dev &#061; mrt-&gt;vif_table[mrt-&gt;mroute_reg_vif_num].dev;<br \/>\nread_unlock(&amp;mrt_lock);<\/p>\n<p>if (!reg_dev)<br \/>\nreturn 1;<\/p>\n<p>skb-&gt;mac_header &#061; skb-&gt;network_header;<br \/>\nskb_pull(skb, (u8 *)encap &#8211; skb-&gt;data);<br \/>\nskb_reset_network_header(skb);<br \/>\nskb-&gt;protocol &#061; htons(ETH_P_IP);<br \/>\nskb-&gt;ip_summed &#061; CHECKSUM_NONE;<\/p>\n<p>skb_tunnel_rx(skb, reg_dev, dev_net(reg_dev));<\/p>\n<p>netif_rx(skb);<\/p>\n<p>return NET_RX_SUCCESS;<br \/>\n}<br \/>\n#else<br \/>\nstatic struct net_device *ipmr_reg_vif(struct net *net, struct mr_table *mrt)<br \/>\n{<br \/>\nreturn NULL;<br \/>\n}<br \/>\n#endif<\/p>\n<p>static int call_ipmr_vif_entry_notifiers(struct net *net,<br \/>\n enum fib_event_type event_type,<br \/>\n struct vif_device *vif,<br \/>\n vifi_t vif_index, u32 tb_id)<br \/>\n{<br \/>\nreturn mr_call_vif_notifiers(net, RTNL_FAMILY_IPMR, event_type,<br \/>\n     vif, vif_index, tb_id,<br \/>\n     &amp;net-&gt;ipv4.ipmr_seq);<br \/>\n}<\/p>\n<p>static int call_ipmr_mfc_entry_notifiers(struct net *net,<br \/>\n enum fib_event_type event_type,<br \/>\n struct mfc_cache *mfc, u32 tb_id)<br \/>\n{<br \/>\nreturn mr_call_mfc_notifiers(net, RTNL_FAMILY_IPMR, event_type,<br \/>\n     &amp;mfc-&gt;_c, tb_id, &amp;net-&gt;ipv4.ipmr_seq);<br \/>\n}<\/p>\n<p>\/**<br \/>\n *vif_delete &#8211; Delete a VIF entry<br \/>\n *&#064;notify: Set to 1, if the caller is a notifier_call<br \/>\n *\/<br \/>\nstatic int vif_delete(struct mr_table *mrt, int vifi, int notify,<br \/>\n      struct list_head *head)<br \/>\n{<br \/>\nstruct net *net &#061; read_pnet(&amp;mrt-&gt;net);<br \/>\nstruct vif_device *v;<br \/>\nstruct net_device *dev;<br \/>\nstruct in_device *in_dev;<\/p>\n<p>if (vifi &lt; 0 || vifi &gt;&#061; mrt-&gt;maxvif)<br \/>\nreturn -EADDRNOTAVAIL;<\/p>\n<p>v &#061; &amp;mrt-&gt;vif_table[vifi];<\/p>\n<p>if (VIF_EXISTS(mrt, vifi))<br \/>\ncall_ipmr_vif_entry_notifiers(net, FIB_EVENT_VIF_DEL, v, vifi,<br \/>\n      mrt-&gt;id);<\/p>\n<p>write_lock_bh(&amp;mrt_lock);<br \/>\ndev &#061; v-&gt;dev;<br \/>\nv-&gt;dev &#061; NULL;<\/p>\n<p>if (!dev) {<br \/>\nwrite_unlock_bh(&amp;mrt_lock);<br \/>\nreturn -EADDRNOTAVAIL;<br \/>\n}<\/p>\n<p>if (vifi &#061;&#061; mrt-&gt;mroute_reg_vif_num)<br \/>\nmrt-&gt;mroute_reg_vif_num &#061; -1;<\/p>\n<p>if (vifi &#043; 1 &#061;&#061; mrt-&gt;maxvif) {<br \/>\nint tmp;<\/p>\n<p>for (tmp &#061; vifi &#8211; 1; tmp &gt;&#061; 0; tmp&#8211;) {<br \/>\nif (VIF_EXISTS(mrt, tmp))<br \/>\nbreak;<br \/>\n}<br \/>\nmrt-&gt;maxvif &#061; tmp&#043;1;<br \/>\n}<\/p>\n<p>write_unlock_bh(&amp;mrt_lock);<\/p>\n<p>dev_set_allmulti(dev, -1);<\/p>\n<p>in_dev &#061; __in_dev_get_rtnl(dev);<br \/>\nif (in_dev) {<br \/>\nIPV4_DEVCONF(in_dev-&gt;cnf, MC_FORWARDING)&#8211;;<br \/>\ninet_netconf_notify_devconf(dev_net(dev), RTM_NEWNETCONF,<br \/>\n    NETCONFA_MC_FORWARDING,<br \/>\n    dev-&gt;ifindex, &amp;in_dev-&gt;cnf);<br \/>\nip_rt_multicast_event(in_dev);<br \/>\n}<\/p>\n<p>if (v-&gt;flags &amp; (VIFF_TUNNEL | VIFF_REGISTER) &amp;&amp; !notify)<br \/>\nunregister_netdevice_queue(dev, head);<\/p>\n<p>dev_put(dev);<br \/>\nreturn 0;<br \/>\n}<\/p>\n<p>static void ipmr_cache_free_rcu(struct rcu_head *head)<br \/>\n{<br \/>\nstruct mr_mfc *c &#061; container_of(head, struct mr_mfc, rcu);<\/p>\n<p>kmem_cache_free(mrt_cachep, (struct mfc_cache *)c);<br \/>\n}<\/p>\n<p>static void ipmr_cache_free(struct mfc_cache *c)<br \/>\n{<br \/>\ncall_rcu(&amp;c-&gt;_c.rcu, ipmr_cache_free_rcu);<br \/>\n}<\/p>\n<p>\/* Destroy an unresolved cache entry, killing queued skbs<br \/>\n * and reporting error to netlink readers.<br \/>\n *\/<br \/>\nstatic void ipmr_destroy_unres(struct mr_table *mrt, struct mfc_cache *c)<br \/>\n{<br \/>\nstruct net *net &#061; read_pnet(&amp;mrt-&gt;net);<br \/>\nstruct sk_buff *skb;<br \/>\nstruct nlmsgerr *e;<\/p>\n<p>atomic_dec(&amp;mrt-&gt;cache_resolve_queue_len);<\/p>\n<p>while ((skb &#061; skb_dequeue(&amp;c-&gt;_c.mfc_un.unres.unresolved))) {<br \/>\nif (ip_hdr(skb)-&gt;version &#061;&#061; 0) {<br \/>\nstruct nlmsghdr *nlh &#061; skb_pull(skb,<br \/>\nsizeof(struct iphdr));<br \/>\nnlh-&gt;nlmsg_type &#061; NLMSG_ERROR;<br \/>\nnlh-&gt;nlmsg_len &#061; nlmsg_msg_size(sizeof(struct nlmsgerr));<br \/>\nskb_trim(skb, nlh-&gt;nlmsg_len);<br \/>\ne &#061; nlmsg_data(nlh);<br \/>\ne-&gt;error &#061; -ETIMEDOUT;<br \/>\nmemset(&amp;e-&gt;msg, 0, sizeof(e-&gt;msg));<\/p>\n<p>rtnl_unicast(skb, net, NETLINK_CB(skb).portid);<br \/>\n} else {<br \/>\nkfree_skb(skb);<br \/>\n}<br \/>\n}<\/p>\n<p>ipmr_cache_free(c);<br \/>\n}<\/p>\n<p>\/* Timer process for the unresolved queue. *\/<br \/>\nstatic void ipmr_expire_process(struct timer_list *t)<br \/>\n{<br \/>\nstruct mr_table *mrt &#061; from_timer(mrt, t, ipmr_expire_timer);<br \/>\nstruct mr_mfc *c, *next;<br \/>\nunsigned long expires;<br \/>\nunsigned long now;<\/p>\n<p>if (!spin_trylock(&amp;mfc_unres_lock)) {<br \/>\nmod_timer(&amp;mrt-&gt;ipmr_expire_timer, jiffies&#043;HZ\/10);<br \/>\nreturn;<br \/>\n}<\/p>\n<p>if (list_empty(&amp;mrt-&gt;mfc_unres_queue))<br \/>\ngoto out;<\/p>\n<p>now &#061; jiffies;<br \/>\nexpires &#061; 10*HZ;<\/p>\n<p>list_for_each_entry_safe(c, next, &amp;mrt-&gt;mfc_unres_queue, list) {<br \/>\nif (time_after(c-&gt;mfc_un.unres.expires, now)) {<br \/>\nunsigned long interval &#061; c-&gt;mfc_un.unres.expires &#8211; now;<br \/>\nif (interval &lt; expires)<br \/>\nexpires &#061; interval;<br \/>\ncontinue;<br \/>\n}<\/p>\n<p>list_del(&amp;c-&gt;list);<br \/>\nmroute_netlink_event(mrt, (struct mfc_cache *)c, RTM_DELROUTE);<br \/>\nipmr_destroy_unres(mrt, (struct mfc_cache *)c);<br \/>\n}<\/p>\n<p>if (!list_empty(&amp;mrt-&gt;mfc_unres_queue))<br \/>\nmod_timer(&amp;mrt-&gt;ipmr_expire_timer, jiffies &#043; expires);<\/p>\n<p>out:<br \/>\nspin_unlock(&amp;mfc_unres_lock);<br \/>\n}<\/p>\n<p>\/* Fill oifs list. It is called under write locked mrt_lock. *\/<br \/>\nstatic void ipmr_update_thresholds(struct mr_table *mrt, struct mr_mfc *cache,<br \/>\n   unsigned char *ttls)<br \/>\n{<br \/>\nint vifi;<\/p>\n<p>cache-&gt;mfc_un.res.minvif &#061; MAXVIFS;<br \/>\ncache-&gt;mfc_un.res.maxvif &#061; 0;<br \/>\nmemset(cache-&gt;mfc_un.res.ttls, 255, MAXVIFS);<\/p>\n<p>for (vifi &#061; 0; vifi &lt; mrt-&gt;maxvif; vifi&#043;&#043;) {<br \/>\nif (VIF_EXISTS(mrt, vifi) &amp;&amp;<br \/>\n    ttls[vifi] &amp;&amp; ttls[vifi] &lt; 255) {<br \/>\ncache-&gt;mfc_un.res.ttls[vifi] &#061; ttls[vifi];<br \/>\nif (cache-&gt;mfc_un.res.minvif &gt; vifi)<br \/>\ncache-&gt;mfc_un.res.minvif &#061; vifi;<br \/>\nif (cache-&gt;mfc_un.res.maxvif &lt;&#061; vifi)<br \/>\ncache-&gt;mfc_un.res.maxvif &#061; vifi &#043; 1;<br \/>\n}<br \/>\n}<br \/>\ncache-&gt;mfc_un.res.lastuse &#061; jiffies;<br \/>\n}<\/p>\n<p>static int vif_add(struct net *net, struct mr_table *mrt,<br \/>\n   struct vifctl *vifc, int mrtsock)<br \/>\n{<br \/>\nint vifi &#061; vifc-&gt;vifc_vifi;<br \/>\nstruct switchdev_attr attr &#061; {<br \/>\n.id &#061; SWITCHDEV_ATTR_ID_PORT_PARENT_ID,<br \/>\n};<br \/>\nstruct vif_device *v &#061; &amp;mrt-&gt;vif_table[vifi];<br \/>\nstruct net_device *dev;<br \/>\nstruct in_device *in_dev;<br \/>\nint err;<\/p>\n<p>\/* Is vif busy ? *\/<br \/>\nif (VIF_EXISTS(mrt, vifi))<br \/>\nreturn -EADDRINUSE;<\/p>\n<p>switch (vifc-&gt;vifc_flags) {<br \/>\ncase VIFF_REGISTER:<br \/>\nif (!ipmr_pimsm_enabled())<br \/>\nreturn -EINVAL;<br \/>\n\/* Special Purpose VIF in PIM<br \/>\n * All the packets will be sent to the daemon<br \/>\n *\/<br \/>\nif (mrt-&gt;mroute_reg_vif_num &gt;&#061; 0)<br \/>\nreturn -EADDRINUSE;<br \/>\ndev &#061; ipmr_reg_vif(net, mrt);<br \/>\nif (!dev)<br \/>\nreturn -ENOBUFS;<br \/>\nerr &#061; dev_set_allmulti(dev, 1);<br \/>\nif (err) {<br \/>\nunregister_netdevice(dev);<br \/>\ndev_put(dev);<br \/>\nreturn err;<br \/>\n}<br \/>\nbreak;<br \/>\ncase VIFF_TUNNEL:<br \/>\ndev &#061; ipmr_new_tunnel(net, vifc);<br \/>\nif (!dev)<br \/>\nreturn -ENOBUFS;<br \/>\nerr &#061; dev_set_allmulti(dev, 1);<br \/>\nif (err) {<br \/>\nipmr_del_tunnel(dev, vifc);<br \/>\ndev_put(dev);<br \/>\nreturn err;<br \/>\n}<br \/>\nbreak;<br \/>\ncase VIFF_USE_IFINDEX:<br \/>\ncase 0:<br \/>\nif (vifc-&gt;vifc_flags &#061;&#061; VIFF_USE_IFINDEX) {<br \/>\ndev &#061; dev_get_by_index(net, vifc-&gt;vifc_lcl_ifindex);<br \/>\nif (dev &amp;&amp; !__in_dev_get_rtnl(dev)) {<br \/>\ndev_put(dev);<br \/>\nreturn -EADDRNOTAVAIL;<br \/>\n}<br \/>\n} else {<br \/>\ndev &#061; ip_dev_find(net, vifc-&gt;vifc_lcl_addr.s_addr);<br \/>\n}<br \/>\nif (!dev)<br \/>\nreturn -EADDRNOTAVAIL;<br \/>\nerr &#061; dev_set_allmulti(dev, 1);<br \/>\nif (err) {<br \/>\ndev_put(dev);<br \/>\nreturn err;<br \/>\n}<br \/>\nbreak;<br \/>\ndefault:<br \/>\nreturn -EINVAL;<br \/>\n}<\/p>\n<p>in_dev &#061; __in_dev_get_rtnl(dev);<br \/>\nif (!in_dev) {<br \/>\ndev_put(dev);<br \/>\nreturn -EADDRNOTAVAIL;<br \/>\n}<br \/>\nIPV4_DEVCONF(in_dev-&gt;cnf, MC_FORWARDING)&#043;&#043;;<br \/>\ninet_netconf_notify_devconf(net, RTM_NEWNETCONF, NETCONFA_MC_FORWARDING,<br \/>\n    dev-&gt;ifindex, &amp;in_dev-&gt;cnf);<br \/>\nip_rt_multicast_event(in_dev);<\/p>\n<p>\/* Fill in the VIF structures *\/<br \/>\nvif_device_init(v, dev, vifc-&gt;vifc_rate_limit,<br \/>\nvifc-&gt;vifc_threshold,<br \/>\nvifc-&gt;vifc_flags | (!mrtsock ? VIFF_STATIC : 0),<br \/>\n(VIFF_TUNNEL | VIFF_REGISTER));<\/p>\n<p>attr.orig_dev &#061; dev;<br \/>\nif (!switchdev_port_attr_get(dev, &amp;attr)) {<br \/>\nmemcpy(v-&gt;dev_parent_id.id, attr.u.ppid.id, attr.u.ppid.id_len);<br \/>\nv-&gt;dev_parent_id.id_len &#061; attr.u.ppid.id_len;<br \/>\n} else {<br \/>\nv-&gt;dev_parent_id.id_len &#061; 0;<br \/>\n}<\/p>\n<p>v-&gt;local &#061; vifc-&gt;vifc_lcl_addr.s_addr;<br \/>\nv-&gt;remote &#061; vifc-&gt;vifc_rmt_addr.s_addr;<\/p>\n<p>\/* And finish update writing critical data *\/<br \/>\nwrite_lock_bh(&amp;mrt_lock);<br \/>\nv-&gt;dev &#061; dev;<br \/>\nif (v-&gt;flags &amp; VIFF_REGISTER)<br \/>\nmrt-&gt;mroute_reg_vif_num &#061; vifi;<br \/>\nif (vifi&#043;1 &gt; mrt-&gt;maxvif)<br \/>\nmrt-&gt;maxvif &#061; vifi&#043;1;<br \/>\nwrite_unlock_bh(&amp;mrt_lock);<br \/>\ncall_ipmr_vif_entry_notifiers(net, FIB_EVENT_VIF_ADD, v, vifi, mrt-&gt;id);<br \/>\nreturn 0;<br \/>\n}<\/p>\n<p>\/* called with rcu_read_lock() *\/<br \/>\nstatic struct mfc_cache *ipmr_cache_find(struct mr_table *mrt,<br \/>\n __be32 origin,<br \/>\n __be32 mcastgrp)<br \/>\n{<br \/>\nstruct mfc_cache_cmp_arg arg &#061; {<br \/>\n.mfc_mcastgrp &#061; mcastgrp,<br \/>\n.mfc_origin &#061; origin<br \/>\n};<\/p>\n<p>return mr_mfc_find(mrt, &amp;arg);<br \/>\n}<\/p>\n<p>\/* Look for a (*,G) entry *\/<br \/>\nstatic struct mfc_cache *ipmr_cache_find_any(struct mr_table *mrt,<br \/>\n     __be32 mcastgrp, int vifi)<br \/>\n{<br \/>\nstruct mfc_cache_cmp_arg arg &#061; {<br \/>\n.mfc_mcastgrp &#061; mcastgrp,<br \/>\n.mfc_origin &#061; htonl(INADDR_ANY)<br \/>\n};<\/p>\n<p>if (mcastgrp &#061;&#061; htonl(INADDR_ANY))<br \/>\nreturn mr_mfc_find_any_parent(mrt, vifi);<br \/>\nreturn mr_mfc_find_any(mrt, vifi, &amp;arg);<br \/>\n}<\/p>\n<p>\/* Look for a (S,G,iif) entry if parent !&#061; -1 *\/<br \/>\nstatic struct mfc_cache *ipmr_cache_find_parent(struct mr_table *mrt,<br \/>\n__be32 origin, __be32 mcastgrp,<br \/>\nint parent)<br \/>\n{<br \/>\nstruct mfc_cache_cmp_arg arg &#061; {<br \/>\n.mfc_mcastgrp &#061; mcastgrp,<br \/>\n.mfc_origin &#061; origin,<br \/>\n};<\/p>\n<p>return mr_mfc_find_parent(mrt, &amp;arg, parent);<br \/>\n}<\/p>\n<p>\/* Allocate a multicast cache entry *\/<br \/>\nstatic struct mfc_cache *ipmr_cache_alloc(void)<br \/>\n{<br \/>\nstruct mfc_cache *c &#061; kmem_cache_zalloc(mrt_cachep, GFP_KERNEL);<\/p>\n<p>if (c) {<br \/>\nc-&gt;_c.mfc_un.res.last_assert &#061; jiffies &#8211; MFC_ASSERT_THRESH &#8211; 1;<br \/>\nc-&gt;_c.mfc_un.res.minvif &#061; MAXVIFS;<br \/>\nc-&gt;_c.free &#061; ipmr_cache_free_rcu;<br \/>\nrefcount_set(&amp;c-&gt;_c.mfc_un.res.refcount, 1);<br \/>\n}<br \/>\nreturn c;<br \/>\n}<\/p>\n<p>static struct mfc_cache *ipmr_cache_alloc_unres(void)<br \/>\n{<br \/>\nstruct mfc_cache *c &#061; kmem_cache_zalloc(mrt_cachep, GFP_ATOMIC);<\/p>\n<p>if (c) {<br \/>\nskb_queue_head_init(&amp;c-&gt;_c.mfc_un.unres.unresolved);<br \/>\nc-&gt;_c.mfc_un.unres.expires &#061; jiffies &#043; 10 * HZ;<br \/>\n}<br \/>\nreturn c;<br \/>\n}<\/p>\n<p>\/* A cache entry has gone into a resolved state from queued *\/<br \/>\nstatic void ipmr_cache_resolve(struct net *net, struct mr_table *mrt,<br \/>\n       struct mfc_cache *uc, struct mfc_cache *c)<br \/>\n{<br \/>\nstruct sk_buff *skb;<br \/>\nstruct nlmsgerr *e;<\/p>\n<p>\/* Play the pending entries through our router *\/<br \/>\nwhile ((skb &#061; __skb_dequeue(&amp;uc-&gt;_c.mfc_un.unres.unresolved))) {<br \/>\nif (ip_hdr(skb)-&gt;version &#061;&#061; 0) {<br \/>\nstruct nlmsghdr *nlh &#061; skb_pull(skb,<br \/>\nsizeof(struct iphdr));<\/p>\n<p>if (mr_fill_mroute(mrt, skb, &amp;c-&gt;_c,<br \/>\n   nlmsg_data(nlh)) &gt; 0) {<br \/>\nnlh-&gt;nlmsg_len &#061; skb_tail_pointer(skb) &#8211;<br \/>\n (u8 *)nlh;<br \/>\n} else {<br \/>\nnlh-&gt;nlmsg_type &#061; NLMSG_ERROR;<br \/>\nnlh-&gt;nlmsg_len &#061; nlmsg_msg_size(sizeof(struct nlmsgerr));<br \/>\nskb_trim(skb, nlh-&gt;nlmsg_len);<br \/>\ne &#061; nlmsg_data(nlh);<br \/>\ne-&gt;error &#061; -EMSGSIZE;<br \/>\nmemset(&amp;e-&gt;msg, 0, sizeof(e-&gt;msg));<br \/>\n}<\/p>\n<p>rtnl_unicast(skb, net, NETLINK_CB(skb).portid);<br \/>\n} else {<br \/>\nip_mr_forward(net, mrt, skb-&gt;dev, skb, c, 0);<br \/>\n}<br \/>\n}<br \/>\n}<\/p>\n<p>\/* Bounce a cache query up to mrouted and netlink.<br \/>\n *<br \/>\n * Called under mrt_lock.<br \/>\n *\/<br \/>\nstatic int ipmr_cache_report(struct mr_table *mrt,<br \/>\n     struct sk_buff *pkt, vifi_t vifi, int assert)<br \/>\n{<br \/>\nconst int ihl &#061; ip_hdrlen(pkt);<br \/>\nstruct sock *mroute_sk;<br \/>\nstruct igmphdr *igmp;<br \/>\nstruct igmpmsg *msg;<br \/>\nstruct sk_buff *skb;<br \/>\nint ret;<\/p>\n<p>if (assert &#061;&#061; IGMPMSG_WHOLEPKT || assert &#061;&#061; IGMPMSG_WRVIFWHOLE)<br \/>\nskb &#061; skb_realloc_headroom(pkt, sizeof(struct iphdr));<br \/>\nelse<br \/>\nskb &#061; alloc_skb(128, GFP_ATOMIC);<\/p>\n<p>if (!skb)<br \/>\nreturn -ENOBUFS;<\/p>\n<p>if (assert &#061;&#061; IGMPMSG_WHOLEPKT || assert &#061;&#061; IGMPMSG_WRVIFWHOLE) {<br \/>\n\/* Ugly, but we have no choice with this interface.<br \/>\n * Duplicate old header, fix ihl, length etc.<br \/>\n * And all this only to mangle msg-&gt;im_msgtype and<br \/>\n * to set msg-&gt;im_mbz to &#034;mbz&#034; \ud83d\ude42<br \/>\n *\/<br \/>\nskb_push(skb, sizeof(struct iphdr));<br \/>\nskb_reset_network_header(skb);<br \/>\nskb_reset_transport_header(skb);<br \/>\nmsg &#061; (struct igmpmsg *)skb_network_header(skb);<br \/>\nmemcpy(msg, skb_network_header(pkt), sizeof(struct iphdr));<br \/>\nmsg-&gt;im_msgtype &#061; assert;<br \/>\nmsg-&gt;im_mbz &#061; 0;<br \/>\nif (assert &#061;&#061; IGMPMSG_WRVIFWHOLE)<br \/>\nmsg-&gt;im_vif &#061; vifi;<br \/>\nelse<br \/>\nmsg-&gt;im_vif &#061; mrt-&gt;mroute_reg_vif_num;<br \/>\nip_hdr(skb)-&gt;ihl &#061; sizeof(struct iphdr) &gt;&gt; 2;<br \/>\nip_hdr(skb)-&gt;tot_len &#061; htons(ntohs(ip_hdr(pkt)-&gt;tot_len) &#043;<br \/>\n     sizeof(struct iphdr));<br \/>\n} else {<br \/>\n\/* Copy the IP header *\/<br \/>\nskb_set_network_header(skb, skb-&gt;len);<br \/>\nskb_put(skb, ihl);<br \/>\nskb_copy_to_linear_data(skb, pkt-&gt;data, ihl);<br \/>\n\/* Flag to the kernel this is a route add *\/<br \/>\nip_hdr(skb)-&gt;protocol &#061; 0;<br \/>\nmsg &#061; (struct igmpmsg *)skb_network_header(skb);<br \/>\nmsg-&gt;im_vif &#061; vifi;<br \/>\nskb_dst_set(skb, dst_clone(skb_dst(pkt)));<br \/>\n\/* Add our header *\/<br \/>\nigmp &#061; skb_put(skb, sizeof(struct igmphdr));<br \/>\nigmp-&gt;type &#061; assert;<br \/>\nmsg-&gt;im_msgtype &#061; assert;<br \/>\nigmp-&gt;code &#061; 0;<br \/>\nip_hdr(skb)-&gt;tot_len &#061; htons(skb-&gt;len);\/* Fix the length *\/<br \/>\nskb-&gt;transport_header &#061; skb-&gt;network_header;<br \/>\n}<\/p>\n<p>rcu_read_lock();<br \/>\nmroute_sk &#061; rcu_dereference(mrt-&gt;mroute_sk);<br \/>\nif (!mroute_sk) {<br \/>\nrcu_read_unlock();<br \/>\nkfree_skb(skb);<br \/>\nreturn -EINVAL;<br \/>\n}<\/p>\n<p>igmpmsg_netlink_event(mrt, skb);<\/p>\n<p>\/* Deliver to mrouted *\/<br \/>\nret &#061; sock_queue_rcv_skb(mroute_sk, skb);<br \/>\nrcu_read_unlock();<br \/>\nif (ret &lt; 0) {<br \/>\nnet_warn_ratelimited(&#034;mroute: pending queue full, dropping entries\\\\n&#034;);<br \/>\nkfree_skb(skb);<br \/>\n}<\/p>\n<p>return ret;<br \/>\n}<\/p>\n<p>\/* Queue a packet for resolution. It gets locked cache entry! *\/<br \/>\nstatic int ipmr_cache_unresolved(struct mr_table *mrt, vifi_t vifi,<br \/>\n struct sk_buff *skb, struct net_device *dev)<br \/>\n{<br \/>\nconst struct iphdr *iph &#061; ip_hdr(skb);<br \/>\nstruct mfc_cache *c;<br \/>\nbool found &#061; false;<br \/>\nint err;<\/p>\n<p>spin_lock_bh(&amp;mfc_unres_lock);<br \/>\nlist_for_each_entry(c, &amp;mrt-&gt;mfc_unres_queue, _c.list) {<br \/>\nif (c-&gt;mfc_mcastgrp &#061;&#061; iph-&gt;daddr &amp;&amp;<br \/>\n    c-&gt;mfc_origin &#061;&#061; iph-&gt;saddr) {<br \/>\nfound &#061; true;<br \/>\nbreak;<br \/>\n}<br \/>\n}<\/p>\n<p>if (!found) {<br \/>\n\/* Create a new entry if allowable *\/<br \/>\nif (atomic_read(&amp;mrt-&gt;cache_resolve_queue_len) &gt;&#061; 10 ||<br \/>\n    (c &#061; ipmr_cache_alloc_unres()) &#061;&#061; NULL) {<br \/>\nspin_unlock_bh(&amp;mfc_unres_lock);<\/p>\n<p>kfree_skb(skb);<br \/>\nreturn -ENOBUFS;<br \/>\n}<\/p>\n<p>\/* Fill in the new cache entry *\/<br \/>\nc-&gt;_c.mfc_parent &#061; -1;<br \/>\nc-&gt;mfc_origin&#061; iph-&gt;saddr;<br \/>\nc-&gt;mfc_mcastgrp&#061; iph-&gt;daddr;<\/p>\n<p>\/* Reflect first query at mrouted. *\/<br \/>\nerr &#061; ipmr_cache_report(mrt, skb, vifi, IGMPMSG_NOCACHE);<\/p>\n<p>if (err &lt; 0) {<br \/>\n\/* If the report failed throw the cache entry<br \/>\n   out &#8211; Brad Parker<br \/>\n *\/<br \/>\nspin_unlock_bh(&amp;mfc_unres_lock);<\/p>\n<p>ipmr_cache_free(c);<br \/>\nkfree_skb(skb);<br \/>\nreturn err;<br \/>\n}<\/p>\n<p>atomic_inc(&amp;mrt-&gt;cache_resolve_queue_len);<br \/>\nlist_add(&amp;c-&gt;_c.list, &amp;mrt-&gt;mfc_unres_queue);<br \/>\nmroute_netlink_event(mrt, c, RTM_NEWROUTE);<\/p>\n<p>if (atomic_read(&amp;mrt-&gt;cache_resolve_queue_len) &#061;&#061; 1)<br \/>\nmod_timer(&amp;mrt-&gt;ipmr_expire_timer,<br \/>\n  c-&gt;_c.mfc_un.unres.expires);<br \/>\n}<\/p>\n<p>\/* See if we can append the packet *\/<br \/>\nif (c-&gt;_c.mfc_un.unres.unresolved.qlen &gt; 3) {<br \/>\nkfree_skb(skb);<br \/>\nerr &#061; -ENOBUFS;<br \/>\n} else {<br \/>\nif (dev) {<br \/>\nskb-&gt;dev &#061; dev;<br \/>\nskb-&gt;skb_iif &#061; dev-&gt;ifindex;<br \/>\n}<br \/>\nskb_queue_tail(&amp;c-&gt;_c.mfc_un.unres.unresolved, skb);<br \/>\nerr &#061; 0;<br \/>\n}<\/p>\n<p>spin_unlock_bh(&amp;mfc_unres_lock);<br \/>\nreturn err;<br \/>\n}<\/p>\n<p>\/* MFC cache manipulation by user space mroute daemon *\/<\/p>\n<p>static int ipmr_mfc_delete(struct mr_table *mrt, struct mfcctl *mfc, int parent)<br \/>\n{<br \/>\nstruct net *net &#061; read_pnet(&amp;mrt-&gt;net);<br \/>\nstruct mfc_cache *c;<\/p>\n<p>\/* The entries are added\/deleted only under RTNL *\/<br \/>\nrcu_read_lock();<br \/>\nc &#061; ipmr_cache_find_parent(mrt, mfc-&gt;mfcc_origin.s_addr,<br \/>\n   mfc-&gt;mfcc_mcastgrp.s_addr, parent);<br \/>\nrcu_read_unlock();<br \/>\nif (!c)<br \/>\nreturn -ENOENT;<br \/>\nrhltable_remove(&amp;mrt-&gt;mfc_hash, &amp;c-&gt;_c.mnode, ipmr_rht_params);<br \/>\nlist_del_rcu(&amp;c-&gt;_c.list);<br \/>\ncall_ipmr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, c, mrt-&gt;id);<br \/>\nmroute_netlink_event(mrt, c, RTM_DELROUTE);<br \/>\nmr_cache_put(&amp;c-&gt;_c);<\/p>\n<p>return 0;<br \/>\n}<\/p>\n<p>static int ipmr_mfc_add(struct net *net, struct mr_table *mrt,<br \/>\nstruct mfcctl *mfc, int mrtsock, int parent)<br \/>\n{<br \/>\nstruct mfc_cache *uc, *c;<br \/>\nstruct mr_mfc *_uc;<br \/>\nbool found;<br \/>\nint ret;<\/p>\n<p>if (mfc-&gt;mfcc_parent &gt;&#061; MAXVIFS)<br \/>\nreturn -ENFILE;<\/p>\n<p>\/* The entries are added\/deleted only under RTNL *\/<br \/>\nrcu_read_lock();<br \/>\nc &#061; ipmr_cache_find_parent(mrt, mfc-&gt;mfcc_origin.s_addr,<br \/>\n   mfc-&gt;mfcc_mcastgrp.s_addr, parent);<br \/>\nrcu_read_unlock();<br \/>\nif (c) {<br \/>\nwrite_lock_bh(&amp;mrt_lock);<br \/>\nc-&gt;_c.mfc_parent &#061; mfc-&gt;mfcc_parent;<br \/>\nipmr_update_thresholds(mrt, &amp;c-&gt;_c, mfc-&gt;mfcc_ttls);<br \/>\nif (!mrtsock)<br \/>\nc-&gt;_c.mfc_flags |&#061; MFC_STATIC;<br \/>\nwrite_unlock_bh(&amp;mrt_lock);<br \/>\ncall_ipmr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_REPLACE, c,<br \/>\n      mrt-&gt;id);<br \/>\nmroute_netlink_event(mrt, c, RTM_NEWROUTE);<br \/>\nreturn 0;<br \/>\n}<\/p>\n<p>if (mfc-&gt;mfcc_mcastgrp.s_addr !&#061; htonl(INADDR_ANY) &amp;&amp;<br \/>\n    !ipv4_is_multicast(mfc-&gt;mfcc_mcastgrp.s_addr))<br \/>\nreturn -EINVAL;<\/p>\n<p>c &#061; ipmr_cache_alloc();<br \/>\nif (!c)<br \/>\nreturn -ENOMEM;<\/p>\n<p>c-&gt;mfc_origin &#061; mfc-&gt;mfcc_origin.s_addr;<br \/>\nc-&gt;mfc_mcastgrp &#061; mfc-&gt;mfcc_mcastgrp.s_addr;<br \/>\nc-&gt;_c.mfc_parent &#061; mfc-&gt;mfcc_parent;<br \/>\nipmr_update_thresholds(mrt, &amp;c-&gt;_c, mfc-&gt;mfcc_ttls);<br \/>\nif (!mrtsock)<br \/>\nc-&gt;_c.mfc_flags |&#061; MFC_STATIC;<\/p>\n<p>ret &#061; rhltable_insert_key(&amp;mrt-&gt;mfc_hash, &amp;c-&gt;cmparg, &amp;c-&gt;_c.mnode,<br \/>\n  ipmr_rht_params);<br \/>\nif (ret) {<br \/>\npr_err(&#034;ipmr: rhtable insert error %d\\\\n&#034;, ret);<br \/>\nipmr_cache_free(c);<br \/>\nreturn ret;<br \/>\n}<br \/>\nlist_add_tail_rcu(&amp;c-&gt;_c.list, &amp;mrt-&gt;mfc_cache_list);<br \/>\n\/* Check to see if we resolved a queued list. If so we<br \/>\n * need to send on the frames and tidy up.<br \/>\n *\/<br \/>\nfound &#061; false;<br \/>\nspin_lock_bh(&amp;mfc_unres_lock);<br \/>\nlist_for_each_entry(_uc, &amp;mrt-&gt;mfc_unres_queue, list) {<br \/>\nuc &#061; (struct mfc_cache *)_uc;<br \/>\nif (uc-&gt;mfc_origin &#061;&#061; c-&gt;mfc_origin &amp;&amp;<br \/>\n    uc-&gt;mfc_mcastgrp &#061;&#061; c-&gt;mfc_mcastgrp) {<br \/>\nlist_del(&amp;_uc-&gt;list);<br \/>\natomic_dec(&amp;mrt-&gt;cache_resolve_queue_len);<br \/>\nfound &#061; true;<br \/>\nbreak;<br \/>\n}<br \/>\n}<br \/>\nif (list_empty(&amp;mrt-&gt;mfc_unres_queue))<br \/>\ndel_timer(&amp;mrt-&gt;ipmr_expire_timer);<br \/>\nspin_unlock_bh(&amp;mfc_unres_lock);<\/p>\n<p>if (found) {<br \/>\nipmr_cache_resolve(net, mrt, uc, c);<br \/>\nipmr_cache_free(uc);<br \/>\n}<br \/>\ncall_ipmr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_ADD, c, mrt-&gt;id);<br \/>\nmroute_netlink_event(mrt, c, RTM_NEWROUTE);<br \/>\nreturn 0;<br \/>\n}<\/p>\n<p>\/* Close the multicast socket, and clear the vif tables etc *\/<br \/>\nstatic void mroute_clean_tables(struct mr_table *mrt, bool all)<br \/>\n{<br \/>\nstruct net *net &#061; read_pnet(&amp;mrt-&gt;net);<br \/>\nstruct mr_mfc *c, *tmp;<br \/>\nstruct mfc_cache *cache;<br \/>\nLIST_HEAD(list);<br \/>\nint i;<\/p>\n<p>\/* Shut down all active vif entries *\/<br \/>\nfor (i &#061; 0; i &lt; mrt-&gt;maxvif; i&#043;&#043;) {<br \/>\nif (!all &amp;&amp; (mrt-&gt;vif_table[i].flags &amp; VIFF_STATIC))<br \/>\ncontinue;<br \/>\nvif_delete(mrt, i, 0, &amp;list);<br \/>\n}<br \/>\nunregister_netdevice_many(&amp;list);<\/p>\n<p>\/* Wipe the cache *\/<br \/>\nlist_for_each_entry_safe(c, tmp, &amp;mrt-&gt;mfc_cache_list, list) {<br \/>\nif (!all &amp;&amp; (c-&gt;mfc_flags &amp; MFC_STATIC))<br \/>\ncontinue;<br \/>\nrhltable_remove(&amp;mrt-&gt;mfc_hash, &amp;c-&gt;mnode, ipmr_rht_params);<br \/>\nlist_del_rcu(&amp;c-&gt;list);<br \/>\ncache &#061; (struct mfc_cache *)c;<br \/>\ncall_ipmr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, cache,<br \/>\n      mrt-&gt;id);<br \/>\nmroute_netlink_event(mrt, cache, RTM_DELROUTE);<br \/>\nmr_cache_put(c);<br \/>\n}<\/p>\n<p>if (atomic_read(&amp;mrt-&gt;cache_resolve_queue_len) !&#061; 0) {<br \/>\nspin_lock_bh(&amp;mfc_unres_lock);<br \/>\nlist_for_each_entry_safe(c, tmp, &amp;mrt-&gt;mfc_unres_queue, list) {<br \/>\nlist_del(&amp;c-&gt;list);<br \/>\ncache &#061; (struct mfc_cache *)c;<br \/>\nmroute_netlink_event(mrt, cache, RTM_DELROUTE);<br \/>\nipmr_destroy_unres(mrt, cache);<br \/>\n}<br \/>\nspin_unlock_bh(&amp;mfc_unres_lock);<br \/>\n}<br \/>\n}<\/p>\n<p>\/* called from ip_ra_control(), before an RCU grace period,<br \/>\n * we dont need to call synchronize_rcu() here<br \/>\n *\/<br \/>\nstatic void mrtsock_destruct(struct sock *sk)<br \/>\n{<br \/>\nstruct net *net &#061; sock_net(sk);<br \/>\nstruct mr_table *mrt;<\/p>\n<p>rtnl_lock();<br \/>\nipmr_for_each_table(mrt, net) {<br \/>\nif (sk &#061;&#061; rtnl_dereference(mrt-&gt;mroute_sk)) {<br \/>\nIPV4_DEVCONF_ALL(net, MC_FORWARDING)&#8211;;<br \/>\ninet_netconf_notify_devconf(net, RTM_NEWNETCONF,<br \/>\n    NETCONFA_MC_FORWARDING,<br \/>\n    NETCONFA_IFINDEX_ALL,<br \/>\n    net-&gt;ipv4.devconf_all);<br \/>\nRCU_INIT_POINTER(mrt-&gt;mroute_sk, NULL);<br \/>\nmroute_clean_tables(mrt, false);<br \/>\n}<br \/>\n}<br \/>\nrtnl_unlock();<br \/>\n}<\/p>\n<p>\/* Socket options and virtual interface manipulation. The whole<br \/>\n * virtual interface system is a complete heap, but unfortunately<br \/>\n * that&#039;s how BSD mrouted happens to think. Maybe one day with a proper<br \/>\n * MOSPF\/PIM router set up we can clean this up.<br \/>\n *\/<\/p>\n<p>int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval,<br \/>\n unsigned int optlen)<br \/>\n{<br \/>\nstruct net *net &#061; sock_net(sk);<br \/>\nint val, ret &#061; 0, parent &#061; 0;<br \/>\nstruct mr_table *mrt;<br \/>\nstruct vifctl vif;<br \/>\nstruct mfcctl mfc;<br \/>\nbool do_wrvifwhole;<br \/>\nu32 uval;<\/p>\n<p>\/* There&#039;s one exception to the lock &#8211; MRT_DONE which needs to unlock *\/<br \/>\nrtnl_lock();<br \/>\nif (sk-&gt;sk_type !&#061; SOCK_RAW ||<br \/>\n    inet_sk(sk)-&gt;inet_num !&#061; IPPROTO_IGMP) {<br \/>\nret &#061; -EOPNOTSUPP;<br \/>\ngoto out_unlock;<br \/>\n}<\/p>\n<p>mrt &#061; ipmr_get_table(net, raw_sk(sk)-&gt;ipmr_table ? : RT_TABLE_DEFAULT);<br \/>\nif (!mrt) {<br \/>\nret &#061; -ENOENT;<br \/>\ngoto out_unlock;<br \/>\n}<br \/>\nif (optname !&#061; MRT_INIT) {<br \/>\nif (sk !&#061; rcu_access_pointer(mrt-&gt;mroute_sk) &amp;&amp;<br \/>\n    !ns_capable(net-&gt;user_ns, CAP_NET_ADMIN)) {<br \/>\nret &#061; -EACCES;<br \/>\ngoto out_unlock;<br \/>\n}<br \/>\n}<\/p>\n<p>switch (optname) {<br \/>\ncase MRT_INIT:<br \/>\nif (optlen !&#061; sizeof(int)) {<br \/>\nret &#061; -EINVAL;<br \/>\nbreak;<br \/>\n}<br \/>\nif (rtnl_dereference(mrt-&gt;mroute_sk)) {<br \/>\nret &#061; -EADDRINUSE;<br \/>\nbreak;<br \/>\n}<\/p>\n<p>ret &#061; ip_ra_control(sk, 1, mrtsock_destruct);<br \/>\nif (ret &#061;&#061; 0) {<br \/>\nrcu_assign_pointer(mrt-&gt;mroute_sk, sk);<br \/>\nIPV4_DEVCONF_ALL(net, MC_FORWARDING)&#043;&#043;;<br \/>\ninet_netconf_notify_devconf(net, RTM_NEWNETCONF,<br \/>\n    NETCONFA_MC_FORWARDING,<br \/>\n    NETCONFA_IFINDEX_ALL,<br \/>\n    net-&gt;ipv4.devconf_all);<br \/>\n}<br \/>\nbreak;<br \/>\ncase MRT_DONE:<br \/>\nif (sk !&#061; rcu_access_pointer(mrt-&gt;mroute_sk)) {<br \/>\nret &#061; -EACCES;<br \/>\n} else {<br \/>\n\/* We need to unlock here because mrtsock_destruct takes<br \/>\n * care of rtnl itself and we can&#039;t change that due to<br \/>\n * the IP_ROUTER_ALERT setsockopt which runs without it.<br \/>\n *\/<br \/>\nrtnl_unlock();<br \/>\nret &#061; ip_ra_control(sk, 0, NULL);<br \/>\ngoto out;<br \/>\n}<br \/>\nbreak;<br \/>\ncase MRT_ADD_VIF:<br \/>\ncase MRT_DEL_VIF:<br \/>\nif (optlen !&#061; sizeof(vif)) {<br \/>\nret &#061; -EINVAL;<br \/>\nbreak;<br \/>\n}<br \/>\nif (copy_from_user(&amp;vif, optval, sizeof(vif))) {<br \/>\nret &#061; -EFAULT;<br \/>\nbreak;<br \/>\n}<br \/>\nif (vif.vifc_vifi &gt;&#061; MAXVIFS) {<br \/>\nret &#061; -ENFILE;<br \/>\nbreak;<br \/>\n}<br \/>\nif (optname &#061;&#061; MRT_ADD_VIF) {<br \/>\nret &#061; vif_add(net, mrt, &amp;vif,<br \/>\n      sk &#061;&#061; rtnl_dereference(mrt-&gt;mroute_sk));<br \/>\n} else {<br \/>\nret &#061; vif_delete(mrt, vif.vifc_vifi, 0, NULL);<br \/>\n}<br \/>\nbreak;<br \/>\n\/* Manipulate the forwarding caches. These live<br \/>\n * in a sort of kernel\/user symbiosis.<br \/>\n *\/<br \/>\ncase MRT_ADD_MFC:<br \/>\ncase MRT_DEL_MFC:<br \/>\nparent &#061; -1;<br \/>\n\/* fall through *\/<br \/>\ncase MRT_ADD_MFC_PROXY:<br \/>\ncase MRT_DEL_MFC_PROXY:<br \/>\nif (optlen !&#061; sizeof(mfc)) {<br \/>\nret &#061; -EINVAL;<br \/>\nbreak;<br \/>\n}<br \/>\nif (copy_from_user(&amp;mfc, optval, sizeof(mfc))) {<br \/>\nret &#061; -EFAULT;<br \/>\nbreak;<br \/>\n}<br \/>\nif (parent &#061;&#061; 0)<br \/>\nparent &#061; mfc.mfcc_parent;<br \/>\nif (optname &#061;&#061; MRT_DEL_MFC || optname &#061;&#061; MRT_DEL_MFC_PROXY)<br \/>\nret &#061; ipmr_mfc_delete(mrt, &amp;mfc, parent);<br \/>\nelse<br \/>\nret &#061; ipmr_mfc_add(net, mrt, &amp;mfc,<br \/>\n   sk &#061;&#061; rtnl_dereference(mrt-&gt;mroute_sk),<br \/>\n   parent);<br \/>\nbreak;<br \/>\n\/* Control PIM assert. *\/<br \/>\ncase MRT_ASSERT:<br \/>\nif (optlen !&#061; sizeof(val)) {<br \/>\nret &#061; -EINVAL;<br \/>\nbreak;<br \/>\n}<br \/>\nif (get_user(val, (int __user *)optval)) {<br \/>\nret &#061; -EFAULT;<br \/>\nbreak;<br \/>\n}<br \/>\nmrt-&gt;mroute_do_assert &#061; val;<br \/>\nbreak;<br \/>\ncase MRT_PIM:<br \/>\nif (!ipmr_pimsm_enabled()) {<br \/>\nret &#061; -ENOPROTOOPT;<br \/>\nbreak;<br \/>\n}<br \/>\nif (optlen !&#061; sizeof(val)) {<br \/>\nret &#061; -EINVAL;<br \/>\nbreak;<br \/>\n}<br \/>\nif (get_user(val, (int __user *)optval)) {<br \/>\nret &#061; -EFAULT;<br \/>\nbreak;<br \/>\n}<\/p>\n<p>do_wrvifwhole &#061; (val &#061;&#061; IGMPMSG_WRVIFWHOLE);<br \/>\nval &#061; !!val;<br \/>\nif (val !&#061; mrt-&gt;mroute_do_pim) {<br \/>\nmrt-&gt;mroute_do_pim &#061; val;<br \/>\nmrt-&gt;mroute_do_assert &#061; val;<br \/>\nmrt-&gt;mroute_do_wrvifwhole &#061; do_wrvifwhole;<br \/>\n}<br \/>\nbreak;<br \/>\ncase MRT_TABLE:<br \/>\nif (!IS_BUILTIN(CONFIG_IP_MROUTE_MULTIPLE_TABLES)) {<br \/>\nret &#061; -ENOPROTOOPT;<br \/>\nbreak;<br \/>\n}<br \/>\nif (optlen !&#061; sizeof(uval)) {<br \/>\nret &#061; -EINVAL;<br \/>\nbreak;<br \/>\n}<br \/>\nif (get_user(uval, (u32 __user *)optval)) {<br \/>\nret &#061; -EFAULT;<br \/>\nbreak;<br \/>\n}<\/p>\n<p>if (sk &#061;&#061; rtnl_dereference(mrt-&gt;mroute_sk)) {<br \/>\nret &#061; -EBUSY;<br \/>\n} else {<br \/>\nmrt &#061; ipmr_new_table(net, uval);<br \/>\nif (IS_ERR(mrt))<br \/>\nret &#061; PTR_ERR(mrt);<br \/>\nelse<br \/>\nraw_sk(sk)-&gt;ipmr_table &#061; uval;<br \/>\n}<br \/>\nbreak;<br \/>\n\/* Spurious command, or MRT_VERSION which you cannot set. *\/<br \/>\ndefault:<br \/>\nret &#061; -ENOPROTOOPT;<br \/>\n}<br \/>\nout_unlock:<br \/>\nrtnl_unlock();<br \/>\nout:<br \/>\nreturn ret;<br \/>\n}<\/p>\n<p>\/* Getsock opt support for the multicast routing system. *\/<br \/>\nint ip_mroute_getsockopt(struct sock *sk, int optname, char __user *optval, int __user *optlen)<br \/>\n{<br \/>\nint olr;<br \/>\nint val;<br \/>\nstruct net *net &#061; sock_net(sk);<br \/>\nstruct mr_table *mrt;<\/p>\n<p>if (sk-&gt;sk_type !&#061; SOCK_RAW ||<br \/>\n    inet_sk(sk)-&gt;inet_num !&#061; IPPROTO_IGMP)<br \/>\nreturn -EOPNOTSUPP;<\/p>\n<p>mrt &#061; ipmr_get_table(net, raw_sk(sk)-&gt;ipmr_table ? : RT_TABLE_DEFAULT);<br \/>\nif (!mrt)<br \/>\nreturn -ENOENT;<\/p>\n<p>switch (optname) {<br \/>\ncase MRT_VERSION:<br \/>\nval &#061; 0x0305;<br \/>\nbreak;<br \/>\ncase MRT_PIM:<br \/>\nif (!ipmr_pimsm_enabled())<br \/>\nreturn -ENOPROTOOPT;<br \/>\nval &#061; mrt-&gt;mroute_do_pim;<br \/>\nbreak;<br \/>\ncase MRT_ASSERT:<br \/>\nval &#061; mrt-&gt;mroute_do_assert;<br \/>\nbreak;<br \/>\ndefault:<br \/>\nreturn -ENOPROTOOPT;<br \/>\n}<\/p>\n<p>if (get_user(olr, optlen))<br \/>\nreturn -EFAULT;<br \/>\nolr &#061; min_t(unsigned int, olr, sizeof(int));<br \/>\nif (olr &lt; 0)<br \/>\nreturn -EINVAL;<br \/>\nif (put_user(olr, optlen))<br \/>\nreturn -EFAULT;<br \/>\nif (copy_to_user(optval, &amp;val, olr))<br \/>\nreturn -EFAULT;<br \/>\nreturn 0;<br \/>\n}<\/p>\n<p>\/* The IP multicast ioctl support routines. *\/<br \/>\nint ipmr_ioctl(struct sock *sk, int cmd, void __user *arg)<br \/>\n{<br \/>\nstruct sioc_sg_req sr;<br \/>\nstruct sioc_vif_req vr;<br \/>\nstruct vif_device *vif;<br \/>\nstruct mfc_cache *c;<br \/>\nstruct net *net &#061; sock_net(sk);<br \/>\nstruct mr_table *mrt;<\/p>\n<p>mrt &#061; ipmr_get_table(net, raw_sk(sk)-&gt;ipmr_table ? : RT_TABLE_DEFAULT);<br \/>\nif (!mrt)<br \/>\nreturn -ENOENT;<\/p>\n<p>switch (cmd) {<br \/>\ncase SIOCGETVIFCNT:<br \/>\nif (copy_from_user(&amp;vr, arg, sizeof(vr)))<br \/>\nreturn -EFAULT;<br \/>\nif (vr.vifi &gt;&#061; mrt-&gt;maxvif)<br \/>\nreturn -EINVAL;<br \/>\nvr.vifi &#061; array_index_nospec(vr.vifi, mrt-&gt;maxvif);<br \/>\nread_lock(&amp;mrt_lock);<br \/>\nvif &#061; &amp;mrt-&gt;vif_table[vr.vifi];<br \/>\nif (VIF_EXISTS(mrt, vr.vifi)) {<br \/>\nvr.icount &#061; vif-&gt;pkt_in;<br \/>\nvr.ocount &#061; vif-&gt;pkt_out;<br \/>\nvr.ibytes &#061; vif-&gt;bytes_in;<br \/>\nvr.obytes &#061; vif-&gt;bytes_out;<br \/>\nread_unlock(&amp;mrt_lock);<\/p>\n<p>if (copy_to_user(arg, &amp;vr, sizeof(vr)))<br \/>\nreturn -EFAULT;<br \/>\nreturn 0;<br \/>\n}<br \/>\nread_unlock(&amp;mrt_lock);<br \/>\nreturn -EADDRNOTAVAIL;<br \/>\ncase SIOCGETSGCNT:<br \/>\nif (copy_from_user(&amp;sr, arg, sizeof(sr)))<br \/>\nreturn -EFAULT;<\/p>\n<p>rcu_read_lock();<br \/>\nc &#061; ipmr_cache_find(mrt, sr.src.s_addr, sr.grp.s_addr);<br \/>\nif (c) {<br \/>\nsr.pktcnt &#061; c-&gt;_c.mfc_un.res.pkt;<br \/>\nsr.bytecnt &#061; c-&gt;_c.mfc_un.res.bytes;<br \/>\nsr.wrong_if &#061; c-&gt;_c.mfc_un.res.wrong_if;<br \/>\nrcu_read_unlock();<\/p>\n<p>if (copy_to_user(arg, &amp;sr, sizeof(sr)))<br \/>\nreturn -EFAULT;<br \/>\nreturn 0;<br \/>\n}<br \/>\nrcu_read_unlock();<br \/>\nreturn -EADDRNOTAVAIL;<br \/>\ndefault:<br \/>\nreturn -ENOIOCTLCMD;<br \/>\n}<br \/>\n}<\/p>\n<p>#ifdef CONFIG_COMPAT<br \/>\nstruct compat_sioc_sg_req {<br \/>\nstruct in_addr src;<br \/>\nstruct in_addr grp;<br \/>\ncompat_ulong_t pktcnt;<br \/>\ncompat_ulong_t bytecnt;<br \/>\ncompat_ulong_t wrong_if;<br \/>\n};<\/p>\n<p>struct compat_sioc_vif_req {<br \/>\nvifi_tvifi;\/* Which iface *\/<br \/>\ncompat_ulong_t icount;<br \/>\ncompat_ulong_t ocount;<br \/>\ncompat_ulong_t ibytes;<br \/>\ncompat_ulong_t obytes;<br \/>\n};<\/p>\n<p>int ipmr_compat_ioctl(struct sock *sk, unsigned int cmd, void __user *arg)<br \/>\n{<br \/>\nstruct compat_sioc_sg_req sr;<br \/>\nstruct compat_sioc_vif_req vr;<br \/>\nstruct vif_device *vif;<br \/>\nstruct mfc_cache *c;<br \/>\nstruct net *net &#061; sock_net(sk);<br \/>\nstruct mr_table *mrt;<\/p>\n<p>mrt &#061; ipmr_get_table(net, raw_sk(sk)-&gt;ipmr_table ? : RT_TABLE_DEFAULT);<br \/>\nif (!mrt)<br \/>\nreturn -ENOENT;<\/p>\n<p>switch (cmd) {<br \/>\ncase SIOCGETVIFCNT:<br \/>\nif (copy_from_user(&amp;vr, arg, sizeof(vr)))<br \/>\nreturn -EFAULT;<br \/>\nif (vr.vifi &gt;&#061; mrt-&gt;maxvif)<br \/>\nreturn -EINVAL;<br \/>\nvr.vifi &#061; array_index_nospec(vr.vifi, mrt-&gt;maxvif);<br \/>\nread_lock(&amp;mrt_lock);<br \/>\nvif &#061; &amp;mrt-&gt;vif_table[vr.vifi];<br \/>\nif (VIF_EXISTS(mrt, vr.vifi)) {<br \/>\nvr.icount &#061; vif-&gt;pkt_in;<br \/>\nvr.ocount &#061; vif-&gt;pkt_out;<br \/>\nvr.ibytes &#061; vif-&gt;bytes_in;<br \/>\nvr.obytes &#061; vif-&gt;bytes_out;<br \/>\nread_unlock(&amp;mrt_lock);<\/p>\n<p>if (copy_to_user(arg, &amp;vr, sizeof(vr)))<br \/>\nreturn -EFAULT;<br \/>\nreturn 0;<br \/>\n}<br \/>\nread_unlock(&amp;mrt_lock);<br \/>\nreturn -EADDRNOTAVAIL;<br \/>\ncase SIOCGETSGCNT:<br \/>\nif (copy_from_user(&amp;sr, arg, sizeof(sr)))<br \/>\nreturn -EFAULT;<\/p>\n<p>rcu_read_lock();<br \/>\nc &#061; ipmr_cache_find(mrt, sr.src.s_addr, sr.grp.s_addr);<br \/>\nif (c) {<br \/>\nsr.pktcnt &#061; c-&gt;_c.mfc_un.res.pkt;<br \/>\nsr.bytecnt &#061; c-&gt;_c.mfc_un.res.bytes;<br \/>\nsr.wrong_if &#061; c-&gt;_c.mfc_un.res.wrong_if;<br \/>\nrcu_read_unlock();<\/p>\n<p>if (copy_to_user(arg, &amp;sr, sizeof(sr)))<br \/>\nreturn -EFAULT;<br \/>\nreturn 0;<br \/>\n}<br \/>\nrcu_read_unlock();<br \/>\nreturn -EADDRNOTAVAIL;<br \/>\ndefault:<br \/>\nreturn -ENOIOCTLCMD;<br \/>\n}<br \/>\n}<br \/>\n#endif<\/p>\n<p>static int ipmr_device_event(struct notifier_block *this, unsigned long event, void *ptr)<br \/>\n{<br \/>\nstruct net_device *dev &#061; netdev_notifier_info_to_dev(ptr);<br \/>\nstruct net *net &#061; dev_net(dev);<br \/>\nstruct mr_table *mrt;<br \/>\nstruct vif_device *v;<br \/>\nint ct;<\/p>\n<p>if (event !&#061; NETDEV_UNREGISTER)<br \/>\nreturn NOTIFY_DONE;<\/p>\n<p>ipmr_for_each_table(mrt, net) {<br \/>\nv &#061; &amp;mrt-&gt;vif_table[0];<br \/>\nfor (ct &#061; 0; ct &lt; mrt-&gt;maxvif; ct&#043;&#043;, v&#043;&#043;) {<br \/>\nif (v-&gt;dev &#061;&#061; dev)<br \/>\nvif_delete(mrt, ct, 1, NULL);<br \/>\n}<br \/>\n}<br \/>\nreturn NOTIFY_DONE;<br \/>\n}<\/p>\n<p>static struct notifier_block ip_mr_notifier &#061; {<br \/>\n.notifier_call &#061; ipmr_device_event,<br \/>\n};<\/p>\n<p>\/* Encapsulate a packet by attaching a valid IPIP header to it.<br \/>\n * This avoids tunnel drivers and other mess and gives us the speed so<br \/>\n * important for multicast video.<br \/>\n *\/<br \/>\nstatic void ip_encap(struct net *net, struct sk_buff *skb,<br \/>\n     __be32 saddr, __be32 daddr)<br \/>\n{<br \/>\nstruct iphdr *iph;<br \/>\nconst struct iphdr *old_iph &#061; ip_hdr(skb);<\/p>\n<p>skb_push(skb, sizeof(struct iphdr));<br \/>\nskb-&gt;transport_header &#061; skb-&gt;network_header;<br \/>\nskb_reset_network_header(skb);<br \/>\niph &#061; ip_hdr(skb);<\/p>\n<p>iph-&gt;version&#061;4;<br \/>\niph-&gt;tos&#061;old_iph-&gt;tos;<br \/>\niph-&gt;ttl&#061;old_iph-&gt;ttl;<br \/>\niph-&gt;frag_off&#061;0;<br \/>\niph-&gt;daddr&#061;daddr;<br \/>\niph-&gt;saddr&#061;saddr;<br \/>\niph-&gt;protocol&#061;IPPROTO_IPIP;<br \/>\niph-&gt;ihl&#061;5;<br \/>\niph-&gt;tot_len&#061;htons(skb-&gt;len);<br \/>\nip_select_ident(net, skb, NULL);<br \/>\nip_send_check(iph);<\/p>\n<p>memset(&amp;(IPCB(skb)-&gt;opt), 0, sizeof(IPCB(skb)-&gt;opt));<br \/>\nnf_reset(skb);<br \/>\n}<\/p>\n<p>static inline int ipmr_forward_finish(struct net *net, struct sock *sk,<br \/>\n      struct sk_buff *skb)<br \/>\n{<br \/>\nstruct ip_options *opt &#061; &amp;(IPCB(skb)-&gt;opt);<\/p>\n<p>IP_INC_STATS(net, IPSTATS_MIB_OUTFORWDATAGRAMS);<br \/>\nIP_ADD_STATS(net, IPSTATS_MIB_OUTOCTETS, skb-&gt;len);<\/p>\n<p>if (unlikely(opt-&gt;optlen))<br \/>\nip_forward_options(skb);<\/p>\n<p>return dst_output(net, sk, skb);<br \/>\n}<\/p>\n<p>#ifdef CONFIG_NET_SWITCHDEV<br \/>\nstatic bool ipmr_forward_offloaded(struct sk_buff *skb, struct mr_table *mrt,<br \/>\n   int in_vifi, int out_vifi)<br \/>\n{<br \/>\nstruct vif_device *out_vif &#061; &amp;mrt-&gt;vif_table[out_vifi];<br \/>\nstruct vif_device *in_vif &#061; &amp;mrt-&gt;vif_table[in_vifi];<\/p>\n<p>if (!skb-&gt;offload_mr_fwd_mark)<br \/>\nreturn false;<br \/>\nif (!out_vif-&gt;dev_parent_id.id_len || !in_vif-&gt;dev_parent_id.id_len)<br \/>\nreturn false;<br \/>\nreturn netdev_phys_item_id_same(&amp;out_vif-&gt;dev_parent_id,<br \/>\n&amp;in_vif-&gt;dev_parent_id);<br \/>\n}<br \/>\n#else<br \/>\nstatic bool ipmr_forward_offloaded(struct sk_buff *skb, struct mr_table *mrt,<br \/>\n   int in_vifi, int out_vifi)<br \/>\n{<br \/>\nreturn false;<br \/>\n}<br \/>\n#endif<\/p>\n<p>\/* Processing handlers for ipmr_forward *\/<\/p>\n<p>static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt,<br \/>\n    int in_vifi, struct sk_buff *skb,<br \/>\n    struct mfc_cache *c, int vifi)<br \/>\n{<br \/>\nconst struct iphdr *iph &#061; ip_hdr(skb);<br \/>\nstruct vif_device *vif &#061; &amp;mrt-&gt;vif_table[vifi];<br \/>\nstruct net_device *dev;<br \/>\nstruct rtable *rt;<br \/>\nstruct flowi4 fl4;<br \/>\nint    encap &#061; 0;<\/p>\n<p>if (!vif-&gt;dev)<br \/>\ngoto out_free;<\/p>\n<p>if (vif-&gt;flags &amp; VIFF_REGISTER) {<br \/>\nvif-&gt;pkt_out&#043;&#043;;<br \/>\nvif-&gt;bytes_out &#043;&#061; skb-&gt;len;<br \/>\nvif-&gt;dev-&gt;stats.tx_bytes &#043;&#061; skb-&gt;len;<br \/>\nvif-&gt;dev-&gt;stats.tx_packets&#043;&#043;;<br \/>\nipmr_cache_report(mrt, skb, vifi, IGMPMSG_WHOLEPKT);<br \/>\ngoto out_free;<br \/>\n}<\/p>\n<p>if (ipmr_forward_offloaded(skb, mrt, in_vifi, vifi))<br \/>\ngoto out_free;<\/p>\n<p>if (vif-&gt;flags &amp; VIFF_TUNNEL) {<br \/>\nrt &#061; ip_route_output_ports(net, &amp;fl4, NULL,<br \/>\n   vif-&gt;remote, vif-&gt;local,<br \/>\n   0, 0,<br \/>\n   IPPROTO_IPIP,<br \/>\n   RT_TOS(iph-&gt;tos), vif-&gt;link);<br \/>\nif (IS_ERR(rt))<br \/>\ngoto out_free;<br \/>\nencap &#061; sizeof(struct iphdr);<br \/>\n} else {<br \/>\nrt &#061; ip_route_output_ports(net, &amp;fl4, NULL, iph-&gt;daddr, 0,<br \/>\n   0, 0,<br \/>\n   IPPROTO_IPIP,<br \/>\n   RT_TOS(iph-&gt;tos), vif-&gt;link);<br \/>\nif (IS_ERR(rt))<br \/>\ngoto out_free;<br \/>\n}<\/p>\n<p>dev &#061; rt-&gt;dst.dev;<\/p>\n<p>if (skb-&gt;len&#043;encap &gt; dst_mtu(&amp;rt-&gt;dst) &amp;&amp; (ntohs(iph-&gt;frag_off) &amp; IP_DF)) {<br \/>\n\/* Do not fragment multicasts. Alas, IPv4 does not<br \/>\n * allow to send ICMP, so that packets will disappear<br \/>\n * to blackhole.<br \/>\n *\/<br \/>\nIP_INC_STATS(net, IPSTATS_MIB_FRAGFAILS);<br \/>\nip_rt_put(rt);<br \/>\ngoto out_free;<br \/>\n}<\/p>\n<p>encap &#043;&#061; LL_RESERVED_SPACE(dev) &#043; rt-&gt;dst.header_len;<\/p>\n<p>if (skb_cow(skb, encap)) {<br \/>\nip_rt_put(rt);<br \/>\ngoto out_free;<br \/>\n}<\/p>\n<p>vif-&gt;pkt_out&#043;&#043;;<br \/>\nvif-&gt;bytes_out &#043;&#061; skb-&gt;len;<\/p>\n<p>skb_dst_drop(skb);<br \/>\nskb_dst_set(skb, &amp;rt-&gt;dst);<br \/>\nip_decrease_ttl(ip_hdr(skb));<\/p>\n<p>\/* FIXME: forward and output firewalls used to be called here.<br \/>\n * What do we do with netfilter? &#8212; RR<br \/>\n *\/<br \/>\nif (vif-&gt;flags &amp; VIFF_TUNNEL) {<br \/>\nip_encap(net, skb, vif-&gt;local, vif-&gt;remote);<br \/>\n\/* FIXME: extra output firewall step used to be here. &#8211;RR *\/<br \/>\nvif-&gt;dev-&gt;stats.tx_packets&#043;&#043;;<br \/>\nvif-&gt;dev-&gt;stats.tx_bytes &#043;&#061; skb-&gt;len;<br \/>\n}<\/p>\n<p>IPCB(skb)-&gt;flags |&#061; IPSKB_FORWARDED;<\/p>\n<p>\/* RFC1584 teaches, that DVMRP\/PIM router must deliver packets locally<br \/>\n * not only before forwarding, but after forwarding on all output<br \/>\n * interfaces. It is clear, if mrouter runs a multicasting<br \/>\n * program, it should receive packets not depending to what interface<br \/>\n * program is joined.<br \/>\n * If we will not make it, the program will have to join on all<br \/>\n * interfaces. On the other hand, multihoming host (or router, but<br \/>\n * not mrouter) cannot join to more than one interface &#8211; it will<br \/>\n * result in receiving multiple packets.<br \/>\n *\/<br \/>\nNF_HOOK(NFPROTO_IPV4, NF_INET_FORWARD,<br \/>\nnet, NULL, skb, skb-&gt;dev, dev,<br \/>\nipmr_forward_finish);<br \/>\nreturn;<\/p>\n<p>out_free:<br \/>\nkfree_skb(skb);<br \/>\n}<\/p>\n<p>static int ipmr_find_vif(struct mr_table *mrt, struct net_device *dev)<br \/>\n{<br \/>\nint ct;<\/p>\n<p>for (ct &#061; mrt-&gt;maxvif-1; ct &gt;&#061; 0; ct&#8211;) {<br \/>\nif (mrt-&gt;vif_table[ct].dev &#061;&#061; dev)<br \/>\nbreak;<br \/>\n}<br \/>\nreturn ct;<br \/>\n}<\/p>\n<p>\/* &#034;local&#034; means that we should preserve one skb (for local delivery) *\/<br \/>\nstatic void ip_mr_forward(struct net *net, struct mr_table *mrt,<br \/>\n  struct net_device *dev, struct sk_buff *skb,<br \/>\n  struct mfc_cache *c, int local)<br \/>\n{<br \/>\nint true_vifi &#061; ipmr_find_vif(mrt, dev);<br \/>\nint psend &#061; -1;<br \/>\nint vif, ct;<\/p>\n<p>vif &#061; c-&gt;_c.mfc_parent;<br \/>\nc-&gt;_c.mfc_un.res.pkt&#043;&#043;;<br \/>\nc-&gt;_c.mfc_un.res.bytes &#043;&#061; skb-&gt;len;<br \/>\nc-&gt;_c.mfc_un.res.lastuse &#061; jiffies;<\/p>\n<p>if (c-&gt;mfc_origin &#061;&#061; htonl(INADDR_ANY) &amp;&amp; true_vifi &gt;&#061; 0) {<br \/>\nstruct mfc_cache *cache_proxy;<\/p>\n<p>\/* For an (*,G) entry, we only check that the incomming<br \/>\n * interface is part of the static tree.<br \/>\n *\/<br \/>\ncache_proxy &#061; mr_mfc_find_any_parent(mrt, vif);<br \/>\nif (cache_proxy &amp;&amp;<br \/>\n    cache_proxy-&gt;_c.mfc_un.res.ttls[true_vifi] &lt; 255)<br \/>\ngoto forward;<br \/>\n}<\/p>\n<p>\/* Wrong interface: drop packet and (maybe) send PIM assert. *\/<br \/>\nif (mrt-&gt;vif_table[vif].dev !&#061; dev) {<br \/>\nif (rt_is_output_route(skb_rtable(skb))) {<br \/>\n\/* It is our own packet, looped back.<br \/>\n * Very complicated situation&#8230;<br \/>\n *<br \/>\n * The best workaround until routing daemons will be<br \/>\n * fixed is not to redistribute packet, if it was<br \/>\n * send through wrong interface. It means, that<br \/>\n * multicast applications WILL NOT work for<br \/>\n * (S,G), which have default multicast route pointing<br \/>\n * to wrong oif. In any case, it is not a good<br \/>\n * idea to use multicasting applications on router.<br \/>\n *\/<br \/>\ngoto dont_forward;<br \/>\n}<\/p>\n<p>c-&gt;_c.mfc_un.res.wrong_if&#043;&#043;;<\/p>\n<p>if (true_vifi &gt;&#061; 0 &amp;&amp; mrt-&gt;mroute_do_assert &amp;&amp;<br \/>\n    \/* pimsm uses asserts, when switching from RPT to SPT,<br \/>\n     * so that we cannot check that packet arrived on an oif.<br \/>\n     * It is bad, but otherwise we would need to move pretty<br \/>\n     * large chunk of pimd to kernel. Ough&#8230; &#8211;ANK<br \/>\n     *\/<br \/>\n    (mrt-&gt;mroute_do_pim ||<br \/>\n     c-&gt;_c.mfc_un.res.ttls[true_vifi] &lt; 255) &amp;&amp;<br \/>\n    time_after(jiffies,<br \/>\n       c-&gt;_c.mfc_un.res.last_assert &#043;<br \/>\n       MFC_ASSERT_THRESH)) {<br \/>\nc-&gt;_c.mfc_un.res.last_assert &#061; jiffies;<br \/>\nipmr_cache_report(mrt, skb, true_vifi, IGMPMSG_WRONGVIF);<br \/>\nif (mrt-&gt;mroute_do_wrvifwhole)<br \/>\nipmr_cache_report(mrt, skb, true_vifi,<br \/>\n  IGMPMSG_WRVIFWHOLE);<br \/>\n}<br \/>\ngoto dont_forward;<br \/>\n}<\/p>\n<p>forward:<br \/>\nmrt-&gt;vif_table[vif].pkt_in&#043;&#043;;<br \/>\nmrt-&gt;vif_table[vif].bytes_in &#043;&#061; skb-&gt;len;<\/p>\n<p>\/* Forward the frame *\/<br \/>\nif (c-&gt;mfc_origin &#061;&#061; htonl(INADDR_ANY) &amp;&amp;<br \/>\n    c-&gt;mfc_mcastgrp &#061;&#061; htonl(INADDR_ANY)) {<br \/>\nif (true_vifi &gt;&#061; 0 &amp;&amp;<br \/>\n    true_vifi !&#061; c-&gt;_c.mfc_parent &amp;&amp;<br \/>\n    ip_hdr(skb)-&gt;ttl &gt;<br \/>\nc-&gt;_c.mfc_un.res.ttls[c-&gt;_c.mfc_parent]) {<br \/>\n\/* It&#039;s an (*,*) entry and the packet is not coming from<br \/>\n * the upstream: forward the packet to the upstream<br \/>\n * only.<br \/>\n *\/<br \/>\npsend &#061; c-&gt;_c.mfc_parent;<br \/>\ngoto last_forward;<br \/>\n}<br \/>\ngoto dont_forward;<br \/>\n}<br \/>\nfor (ct &#061; c-&gt;_c.mfc_un.res.maxvif &#8211; 1;<br \/>\n     ct &gt;&#061; c-&gt;_c.mfc_un.res.minvif; ct&#8211;) {<br \/>\n\/* For (*,G) entry, don&#039;t forward to the incoming interface *\/<br \/>\nif ((c-&gt;mfc_origin !&#061; htonl(INADDR_ANY) ||<br \/>\n     ct !&#061; true_vifi) &amp;&amp;<br \/>\n    ip_hdr(skb)-&gt;ttl &gt; c-&gt;_c.mfc_un.res.ttls[ct]) {<br \/>\nif (psend !&#061; -1) {<br \/>\nstruct sk_buff *skb2 &#061; skb_clone(skb, GFP_ATOMIC);<\/p>\n<p>if (skb2)<br \/>\nipmr_queue_xmit(net, mrt, true_vifi,<br \/>\nskb2, c, psend);<br \/>\n}<br \/>\npsend &#061; ct;<br \/>\n}<br \/>\n}<br \/>\nlast_forward:<br \/>\nif (psend !&#061; -1) {<br \/>\nif (local) {<br \/>\nstruct sk_buff *skb2 &#061; skb_clone(skb, GFP_ATOMIC);<\/p>\n<p>if (skb2)<br \/>\nipmr_queue_xmit(net, mrt, true_vifi, skb2,<br \/>\nc, psend);<br \/>\n} else {<br \/>\nipmr_queue_xmit(net, mrt, true_vifi, skb, c, psend);<br \/>\nreturn;<br \/>\n}<br \/>\n}<\/p>\n<p>dont_forward:<br \/>\nif (!local)<br \/>\nkfree_skb(skb);<br \/>\n}<\/p>\n<p>static struct mr_table *ipmr_rt_fib_lookup(struct net *net, struct sk_buff *skb)<br \/>\n{<br \/>\nstruct rtable *rt &#061; skb_rtable(skb);<br \/>\nstruct iphdr *iph &#061; ip_hdr(skb);<br \/>\nstruct flowi4 fl4 &#061; {<br \/>\n.daddr &#061; iph-&gt;daddr,<br \/>\n.saddr &#061; iph-&gt;saddr,<br \/>\n.flowi4_tos &#061; RT_TOS(iph-&gt;tos),<br \/>\n.flowi4_oif &#061; (rt_is_output_route(rt) ?<br \/>\n       skb-&gt;dev-&gt;ifindex : 0),<br \/>\n.flowi4_iif &#061; (rt_is_output_route(rt) ?<br \/>\n       LOOPBACK_IFINDEX :<br \/>\n       skb-&gt;dev-&gt;ifindex),<br \/>\n.flowi4_mark &#061; skb-&gt;mark,<br \/>\n};<br \/>\nstruct mr_table *mrt;<br \/>\nint err;<\/p>\n<p>err &#061; ipmr_fib_lookup(net, &amp;fl4, &amp;mrt);<br \/>\nif (err)<br \/>\nreturn ERR_PTR(err);<br \/>\nreturn mrt;<br \/>\n}<\/p>\n<p>\/* Multicast packets for forwarding arrive here<br \/>\n * Called with rcu_read_lock();<br \/>\n *\/<br \/>\nint ip_mr_input(struct sk_buff *skb)<br \/>\n{<br \/>\nstruct mfc_cache *cache;<br \/>\nstruct net *net &#061; dev_net(skb-&gt;dev);<br \/>\nint local &#061; skb_rtable(skb)-&gt;rt_flags &amp; RTCF_LOCAL;<br \/>\nstruct mr_table *mrt;<br \/>\nstruct net_device *dev;<\/p>\n<p>\/* skb-&gt;dev passed in is the loX master dev for vrfs.<br \/>\n * As there are no vifs associated with loopback devices,<br \/>\n * get the proper interface that does have a vif associated with it.<br \/>\n *\/<br \/>\ndev &#061; skb-&gt;dev;<br \/>\nif (netif_is_l3_master(skb-&gt;dev)) {<br \/>\ndev &#061; dev_get_by_index_rcu(net, IPCB(skb)-&gt;iif);<br \/>\nif (!dev) {<br \/>\nkfree_skb(skb);<br \/>\nreturn -ENODEV;<br \/>\n}<br \/>\n}<\/p>\n<p>\/* Packet is looped back after forward, it should not be<br \/>\n * forwarded second time, but still can be delivered locally.<br \/>\n *\/<br \/>\nif (IPCB(skb)-&gt;flags &amp; IPSKB_FORWARDED)<br \/>\ngoto dont_forward;<\/p>\n<p>mrt &#061; ipmr_rt_fib_lookup(net, skb);<br \/>\nif (IS_ERR(mrt)) {<br \/>\nkfree_skb(skb);<br \/>\nreturn PTR_ERR(mrt);<br \/>\n}<br \/>\nif (!local) {<br \/>\nif (IPCB(skb)-&gt;opt.router_alert) {<br \/>\nif (ip_call_ra_chain(skb))<br \/>\nreturn 0;<br \/>\n} else if (ip_hdr(skb)-&gt;protocol &#061;&#061; IPPROTO_IGMP) {<br \/>\n\/* IGMPv1 (and broken IGMPv2 implementations sort of<br \/>\n * Cisco IOS &lt;&#061; 11.2(8)) do not put router alert<br \/>\n * option to IGMP packets destined to routable<br \/>\n * groups. It is very bad, because it means<br \/>\n * that we can forward NO IGMP messages.<br \/>\n *\/<br \/>\nstruct sock *mroute_sk;<\/p>\n<p>mroute_sk &#061; rcu_dereference(mrt-&gt;mroute_sk);<br \/>\nif (mroute_sk) {<br \/>\nnf_reset(skb);<br \/>\nraw_rcv(mroute_sk, skb);<br \/>\nreturn 0;<br \/>\n}<br \/>\n    }<br \/>\n}<\/p>\n<p>\/* already under rcu_read_lock() *\/<br \/>\ncache &#061; ipmr_cache_find(mrt, ip_hdr(skb)-&gt;saddr, ip_hdr(skb)-&gt;daddr);<br \/>\nif (!cache) {<br \/>\nint vif &#061; ipmr_find_vif(mrt, dev);<\/p>\n<p>if (vif &gt;&#061; 0)<br \/>\ncache &#061; ipmr_cache_find_any(mrt, ip_hdr(skb)-&gt;daddr,<br \/>\n    vif);<br \/>\n}<\/p>\n<p>\/* No usable cache entry *\/<br \/>\nif (!cache) {<br \/>\nint vif;<\/p>\n<p>if (local) {<br \/>\nstruct sk_buff *skb2 &#061; skb_clone(skb, GFP_ATOMIC);<br \/>\nip_local_deliver(skb);<br \/>\nif (!skb2)<br \/>\nreturn -ENOBUFS;<br \/>\nskb &#061; skb2;<br \/>\n}<\/p>\n<p>read_lock(&amp;mrt_lock);<br \/>\nvif &#061; ipmr_find_vif(mrt, dev);<br \/>\nif (vif &gt;&#061; 0) {<br \/>\nint err2 &#061; ipmr_cache_unresolved(mrt, vif, skb, dev);<br \/>\nread_unlock(&amp;mrt_lock);<\/p>\n<p>return err2;<br \/>\n}<br \/>\nread_unlock(&amp;mrt_lock);<br \/>\nkfree_skb(skb);<br \/>\nreturn -ENODEV;<br \/>\n}<\/p>\n<p>read_lock(&amp;mrt_lock);<br \/>\nip_mr_forward(net, mrt, dev, skb, cache, local);<br \/>\nread_unlock(&amp;mrt_lock);<\/p>\n<p>if (local)<br \/>\nreturn ip_local_deliver(skb);<\/p>\n<p>return 0;<\/p>\n<p>dont_forward:<br \/>\nif (local)<br \/>\nreturn ip_local_deliver(skb);<br \/>\nkfree_skb(skb);<br \/>\nreturn 0;<br \/>\n}<\/p>\n<p>#ifdef CONFIG_IP_PIMSM_V1<br \/>\n\/* Handle IGMP messages of PIMv1 *\/<br \/>\nint pim_rcv_v1(struct sk_buff *skb)<br \/>\n{<br \/>\nstruct igmphdr *pim;<br \/>\nstruct net *net &#061; dev_net(skb-&gt;dev);<br \/>\nstruct mr_table *mrt;<\/p>\n<p>if (!pskb_may_pull(skb, sizeof(*pim) &#043; sizeof(struct iphdr)))<br \/>\ngoto drop;<\/p>\n<p>pim &#061; igmp_hdr(skb);<\/p>\n<p>mrt &#061; ipmr_rt_fib_lookup(net, skb);<br \/>\nif (IS_ERR(mrt))<br \/>\ngoto drop;<br \/>\nif (!mrt-&gt;mroute_do_pim ||<br \/>\n    pim-&gt;group !&#061; PIM_V1_VERSION || pim-&gt;code !&#061; PIM_V1_REGISTER)<br \/>\ngoto drop;<\/p>\n<p>if (__pim_rcv(mrt, skb, sizeof(*pim))) {<br \/>\ndrop:<br \/>\nkfree_skb(skb);<br \/>\n}<br \/>\nreturn 0;<br \/>\n}<br \/>\n#endif<\/p>\n<p>#ifdef CONFIG_IP_PIMSM_V2<br \/>\nstatic int pim_rcv(struct sk_buff *skb)<br \/>\n{<br \/>\nstruct pimreghdr *pim;<br \/>\nstruct net *net &#061; dev_net(skb-&gt;dev);<br \/>\nstruct mr_table *mrt;<\/p>\n<p>if (!pskb_may_pull(skb, sizeof(*pim) &#043; sizeof(struct iphdr)))<br \/>\ngoto drop;<\/p>\n<p>pim &#061; (struct pimreghdr *)skb_transport_header(skb);<br \/>\nif (pim-&gt;type !&#061; ((PIM_VERSION &lt;&lt; 4) | (PIM_TYPE_REGISTER)) ||<br \/>\n    (pim-&gt;flags &amp; PIM_NULL_REGISTER) ||<br \/>\n    (ip_compute_csum((void *)pim, sizeof(*pim)) !&#061; 0 &amp;&amp;<br \/>\n     csum_fold(skb_checksum(skb, 0, skb-&gt;len, 0))))<br \/>\ngoto drop;<\/p>\n<p>mrt &#061; ipmr_rt_fib_lookup(net, skb);<br \/>\nif (IS_ERR(mrt))<br \/>\ngoto drop;<br \/>\nif (__pim_rcv(mrt, skb, sizeof(*pim))) {<br \/>\ndrop:<br \/>\nkfree_skb(skb);<br \/>\n}<br \/>\nreturn 0;<br \/>\n}<br \/>\n#endif<\/p>\n<p>int ipmr_get_route(struct net *net, struct sk_buff *skb,<br \/>\n   __be32 saddr, __be32 daddr,<br \/>\n   struct rtmsg *rtm, u32 portid)<br \/>\n{<br \/>\nstruct mfc_cache *cache;<br \/>\nstruct mr_table *mrt;<br \/>\nint err;<\/p>\n<p>mrt &#061; ipmr_get_table(net, RT_TABLE_DEFAULT);<br \/>\nif (!mrt)<br \/>\nreturn -ENOENT;<\/p>\n<p>rcu_read_lock();<br \/>\ncache &#061; ipmr_cache_find(mrt, saddr, daddr);<br \/>\nif (!cache &amp;&amp; skb-&gt;dev) {<br \/>\nint vif &#061; ipmr_find_vif(mrt, skb-&gt;dev);<\/p>\n<p>if (vif &gt;&#061; 0)<br \/>\ncache &#061; ipmr_cache_find_any(mrt, daddr, vif);<br \/>\n}<br \/>\nif (!cache) {<br \/>\nstruct sk_buff *skb2;<br \/>\nstruct iphdr *iph;<br \/>\nstruct net_device *dev;<br \/>\nint vif &#061; -1;<\/p>\n<p>dev &#061; skb-&gt;dev;<br \/>\nread_lock(&amp;mrt_lock);<br \/>\nif (dev)<br \/>\nvif &#061; ipmr_find_vif(mrt, dev);<br \/>\nif (vif &lt; 0) {<br \/>\nread_unlock(&amp;mrt_lock);<br \/>\nrcu_read_unlock();<br \/>\nreturn -ENODEV;<br \/>\n}<br \/>\nskb2 &#061; skb_clone(skb, GFP_ATOMIC);<br \/>\nif (!skb2) {<br \/>\nread_unlock(&amp;mrt_lock);<br \/>\nrcu_read_unlock();<br \/>\nreturn -ENOMEM;<br \/>\n}<\/p>\n<p>NETLINK_CB(skb2).portid &#061; portid;<br \/>\nskb_push(skb2, sizeof(struct iphdr));<br \/>\nskb_reset_network_header(skb2);<br \/>\niph &#061; ip_hdr(skb2);<br \/>\niph-&gt;ihl &#061; sizeof(struct iphdr) &gt;&gt; 2;<br \/>\niph-&gt;saddr &#061; saddr;<br \/>\niph-&gt;daddr &#061; daddr;<br \/>\niph-&gt;version &#061; 0;<br \/>\nerr &#061; ipmr_cache_unresolved(mrt, vif, skb2, dev);<br \/>\nread_unlock(&amp;mrt_lock);<br \/>\nrcu_read_unlock();<br \/>\nreturn err;<br \/>\n}<\/p>\n<p>read_lock(&amp;mrt_lock);<br \/>\nerr &#061; mr_fill_mroute(mrt, skb, &amp;cache-&gt;_c, rtm);<br \/>\nread_unlock(&amp;mrt_lock);<br \/>\nrcu_read_unlock();<br \/>\nreturn err;<br \/>\n}<\/p>\n<p>static int ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,<br \/>\n    u32 portid, u32 seq, struct mfc_cache *c, int cmd,<br \/>\n    int flags)<br \/>\n{<br \/>\nstruct nlmsghdr *nlh;<br \/>\nstruct rtmsg *rtm;<br \/>\nint err;<\/p>\n<p>nlh &#061; nlmsg_put(skb, portid, seq, cmd, sizeof(*rtm), flags);<br \/>\nif (!nlh)<br \/>\nreturn -EMSGSIZE;<\/p>\n<p>rtm &#061; nlmsg_data(nlh);<br \/>\nrtm-&gt;rtm_family   &#061; RTNL_FAMILY_IPMR;<br \/>\nrtm-&gt;rtm_dst_len  &#061; 32;<br \/>\nrtm-&gt;rtm_src_len  &#061; 32;<br \/>\nrtm-&gt;rtm_tos      &#061; 0;<br \/>\nrtm-&gt;rtm_table    &#061; mrt-&gt;id;<br \/>\nif (nla_put_u32(skb, RTA_TABLE, mrt-&gt;id))<br \/>\ngoto nla_put_failure;<br \/>\nrtm-&gt;rtm_type     &#061; RTN_MULTICAST;<br \/>\nrtm-&gt;rtm_scope    &#061; RT_SCOPE_UNIVERSE;<br \/>\nif (c-&gt;_c.mfc_flags &amp; MFC_STATIC)<br \/>\nrtm-&gt;rtm_protocol &#061; RTPROT_STATIC;<br \/>\nelse<br \/>\nrtm-&gt;rtm_protocol &#061; RTPROT_MROUTED;<br \/>\nrtm-&gt;rtm_flags    &#061; 0;<\/p>\n<p>if (nla_put_in_addr(skb, RTA_SRC, c-&gt;mfc_origin) ||<br \/>\n    nla_put_in_addr(skb, RTA_DST, c-&gt;mfc_mcastgrp))<br \/>\ngoto nla_put_failure;<br \/>\nerr &#061; mr_fill_mroute(mrt, skb, &amp;c-&gt;_c, rtm);<br \/>\n\/* do not break the dump if cache is unresolved *\/<br \/>\nif (err &lt; 0 &amp;&amp; err !&#061; -ENOENT)<br \/>\ngoto nla_put_failure;<\/p>\n<p>nlmsg_end(skb, nlh);<br \/>\nreturn 0;<\/p>\n<p>nla_put_failure:<br \/>\nnlmsg_cancel(skb, nlh);<br \/>\nreturn -EMSGSIZE;<br \/>\n}<\/p>\n<p>static int _ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,<br \/>\n     u32 portid, u32 seq, struct mr_mfc *c, int cmd,<br \/>\n     int flags)<br \/>\n{<br \/>\nreturn ipmr_fill_mroute(mrt, skb, portid, seq, (struct mfc_cache *)c,<br \/>\ncmd, flags);<br \/>\n}<\/p>\n<p>static size_t mroute_msgsize(bool unresolved, int maxvif)<br \/>\n{<br \/>\nsize_t len &#061;<br \/>\nNLMSG_ALIGN(sizeof(struct rtmsg))<br \/>\n&#043; nla_total_size(4)\/* RTA_TABLE *\/<br \/>\n&#043; nla_total_size(4)\/* RTA_SRC *\/<br \/>\n&#043; nla_total_size(4)\/* RTA_DST *\/<br \/>\n;<\/p>\n<p>if (!unresolved)<br \/>\nlen &#061; len<br \/>\n      &#043; nla_total_size(4)\/* RTA_IIF *\/<br \/>\n      &#043; nla_total_size(0)\/* RTA_MULTIPATH *\/<br \/>\n      &#043; maxvif * NLA_ALIGN(sizeof(struct rtnexthop))<br \/>\n\/* RTA_MFC_STATS *\/<br \/>\n      &#043; nla_total_size_64bit(sizeof(struct rta_mfc_stats))<br \/>\n;<\/p>\n<p>return len;<br \/>\n}<\/p>\n<p>static void mroute_netlink_event(struct mr_table *mrt, struct mfc_cache *mfc,<br \/>\n int cmd)<br \/>\n{<br \/>\nstruct net *net &#061; read_pnet(&amp;mrt-&gt;net);<br \/>\nstruct sk_buff *skb;<br \/>\nint err &#061; -ENOBUFS;<\/p>\n<p>skb &#061; nlmsg_new(mroute_msgsize(mfc-&gt;_c.mfc_parent &gt;&#061; MAXVIFS,<br \/>\n       mrt-&gt;maxvif),<br \/>\nGFP_ATOMIC);<br \/>\nif (!skb)<br \/>\ngoto errout;<\/p>\n<p>err &#061; ipmr_fill_mroute(mrt, skb, 0, 0, mfc, cmd, 0);<br \/>\nif (err &lt; 0)<br \/>\ngoto errout;<\/p>\n<p>rtnl_notify(skb, net, 0, RTNLGRP_IPV4_MROUTE, NULL, GFP_ATOMIC);<br \/>\nreturn;<\/p>\n<p>errout:<br \/>\nkfree_skb(skb);<br \/>\nif (err &lt; 0)<br \/>\nrtnl_set_sk_err(net, RTNLGRP_IPV4_MROUTE, err);<br \/>\n}<\/p>\n<p>static size_t igmpmsg_netlink_msgsize(size_t payloadlen)<br \/>\n{<br \/>\nsize_t len &#061;<br \/>\nNLMSG_ALIGN(sizeof(struct rtgenmsg))<br \/>\n&#043; nla_total_size(1)\/* IPMRA_CREPORT_MSGTYPE *\/<br \/>\n&#043; nla_total_size(4)\/* IPMRA_CREPORT_VIF_ID *\/<br \/>\n&#043; nla_total_size(4)\/* IPMRA_CREPORT_SRC_ADDR *\/<br \/>\n&#043; nla_total_size(4)\/* IPMRA_CREPORT_DST_ADDR *\/<br \/>\n\/* IPMRA_CREPORT_PKT *\/<br \/>\n&#043; nla_total_size(payloadlen)<br \/>\n;<\/p>\n<p>return len;<br \/>\n}<\/p>\n<p>static void igmpmsg_netlink_event(struct mr_table *mrt, struct sk_buff *pkt)<br \/>\n{<br \/>\nstruct net *net &#061; read_pnet(&amp;mrt-&gt;net);<br \/>\nstruct nlmsghdr *nlh;<br \/>\nstruct rtgenmsg *rtgenm;<br \/>\nstruct igmpmsg *msg;<br \/>\nstruct sk_buff *skb;<br \/>\nstruct nlattr *nla;<br \/>\nint payloadlen;<\/p>\n<p>payloadlen &#061; pkt-&gt;len &#8211; sizeof(struct igmpmsg);<br \/>\nmsg &#061; (struct igmpmsg *)skb_network_header(pkt);<\/p>\n<p>skb &#061; nlmsg_new(igmpmsg_netlink_msgsize(payloadlen), GFP_ATOMIC);<br \/>\nif (!skb)<br \/>\ngoto errout;<\/p>\n<p>nlh &#061; nlmsg_put(skb, 0, 0, RTM_NEWCACHEREPORT,<br \/>\nsizeof(struct rtgenmsg), 0);<br \/>\nif (!nlh)<br \/>\ngoto errout;<br \/>\nrtgenm &#061; nlmsg_data(nlh);<br \/>\nrtgenm-&gt;rtgen_family &#061; RTNL_FAMILY_IPMR;<br \/>\nif (nla_put_u8(skb, IPMRA_CREPORT_MSGTYPE, msg-&gt;im_msgtype) ||<br \/>\n    nla_put_u32(skb, IPMRA_CREPORT_VIF_ID, msg-&gt;im_vif) ||<br \/>\n    nla_put_in_addr(skb, IPMRA_CREPORT_SRC_ADDR,<br \/>\n    msg-&gt;im_src.s_addr) ||<br \/>\n    nla_put_in_addr(skb, IPMRA_CREPORT_DST_ADDR,<br \/>\n    msg-&gt;im_dst.s_addr))<br \/>\ngoto nla_put_failure;<\/p>\n<p>nla &#061; nla_reserve(skb, IPMRA_CREPORT_PKT, payloadlen);<br \/>\nif (!nla || skb_copy_bits(pkt, sizeof(struct igmpmsg),<br \/>\n  nla_data(nla), payloadlen))<br \/>\ngoto nla_put_failure;<\/p>\n<p>nlmsg_end(skb, nlh);<\/p>\n<p>rtnl_notify(skb, net, 0, RTNLGRP_IPV4_MROUTE_R, NULL, GFP_ATOMIC);<br \/>\nreturn;<\/p>\n<p>nla_put_failure:<br \/>\nnlmsg_cancel(skb, nlh);<br \/>\nerrout:<br \/>\nkfree_skb(skb);<br \/>\nrtnl_set_sk_err(net, RTNLGRP_IPV4_MROUTE_R, -ENOBUFS);<br \/>\n}<\/p>\n<p>static int ipmr_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,<br \/>\n     struct netlink_ext_ack *extack)<br \/>\n{<br \/>\nstruct net *net &#061; sock_net(in_skb-&gt;sk);<br \/>\nstruct nlattr *tb[RTA_MAX &#043; 1];<br \/>\nstruct sk_buff *skb &#061; NULL;<br \/>\nstruct mfc_cache *cache;<br \/>\nstruct mr_table *mrt;<br \/>\nstruct rtmsg *rtm;<br \/>\n__be32 src, grp;<br \/>\nu32 tableid;<br \/>\nint err;<\/p>\n<p>err &#061; nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX,<br \/>\n  rtm_ipv4_policy, extack);<br \/>\nif (err &lt; 0)<br \/>\ngoto errout;<\/p>\n<p>rtm &#061; nlmsg_data(nlh);<\/p>\n<p>src &#061; tb[RTA_SRC] ? nla_get_in_addr(tb[RTA_SRC]) : 0;<br \/>\ngrp &#061; tb[RTA_DST] ? nla_get_in_addr(tb[RTA_DST]) : 0;<br \/>\ntableid &#061; tb[RTA_TABLE] ? nla_get_u32(tb[RTA_TABLE]) : 0;<\/p>\n<p>mrt &#061; ipmr_get_table(net, tableid ? tableid : RT_TABLE_DEFAULT);<br \/>\nif (!mrt) {<br \/>\nerr &#061; -ENOENT;<br \/>\ngoto errout_free;<br \/>\n}<\/p>\n<p>\/* entries are added\/deleted only under RTNL *\/<br \/>\nrcu_read_lock();<br \/>\ncache &#061; ipmr_cache_find(mrt, src, grp);<br \/>\nrcu_read_unlock();<br \/>\nif (!cache) {<br \/>\nerr &#061; -ENOENT;<br \/>\ngoto errout_free;<br \/>\n}<\/p>\n<p>skb &#061; nlmsg_new(mroute_msgsize(false, mrt-&gt;maxvif), GFP_KERNEL);<br \/>\nif (!skb) {<br \/>\nerr &#061; -ENOBUFS;<br \/>\ngoto errout_free;<br \/>\n}<\/p>\n<p>err &#061; ipmr_fill_mroute(mrt, skb, NETLINK_CB(in_skb).portid,<br \/>\n       nlh-&gt;nlmsg_seq, cache,<br \/>\n       RTM_NEWROUTE, 0);<br \/>\nif (err &lt; 0)<br \/>\ngoto errout_free;<\/p>\n<p>err &#061; rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);<\/p>\n<p>errout:<br \/>\nreturn err;<\/p>\n<p>errout_free:<br \/>\nkfree_skb(skb);<br \/>\ngoto errout;<br \/>\n}<\/p>\n<p>static int ipmr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb)<br \/>\n{<br \/>\nreturn mr_rtm_dumproute(skb, cb, ipmr_mr_table_iter,<br \/>\n_ipmr_fill_mroute, &amp;mfc_unres_lock);<br \/>\n}<\/p>\n<p>static const struct nla_policy rtm_ipmr_policy[RTA_MAX &#043; 1] &#061; {<br \/>\n[RTA_SRC]&#061; { .type &#061; NLA_U32 },<br \/>\n[RTA_DST]&#061; { .type &#061; NLA_U32 },<br \/>\n[RTA_IIF]&#061; { .type &#061; NLA_U32 },<br \/>\n[RTA_TABLE]&#061; { .type &#061; NLA_U32 },<br \/>\n[RTA_MULTIPATH]&#061; { .len &#061; sizeof(struct rtnexthop) },<br \/>\n};<\/p>\n<p>static bool ipmr_rtm_validate_proto(unsigned char rtm_protocol)<br \/>\n{<br \/>\nswitch (rtm_protocol) {<br \/>\ncase RTPROT_STATIC:<br \/>\ncase RTPROT_MROUTED:<br \/>\nreturn true;<br \/>\n}<br \/>\nreturn false;<br \/>\n}<\/p>\n<p>static int ipmr_nla_get_ttls(const struct nlattr *nla, struct mfcctl *mfcc)<br \/>\n{<br \/>\nstruct rtnexthop *rtnh &#061; nla_data(nla);<br \/>\nint remaining &#061; nla_len(nla), vifi &#061; 0;<\/p>\n<p>while (rtnh_ok(rtnh, remaining)) {<br \/>\nmfcc-&gt;mfcc_ttls[vifi] &#061; rtnh-&gt;rtnh_hops;<br \/>\nif (&#043;&#043;vifi &#061;&#061; MAXVIFS)<br \/>\nbreak;<br \/>\nrtnh &#061; rtnh_next(rtnh, &amp;remaining);<br \/>\n}<\/p>\n<p>return remaining &gt; 0 ? -EINVAL : vifi;<br \/>\n}<\/p>\n<p>\/* returns &lt; 0 on error, 0 for ADD_MFC and 1 for ADD_MFC_PROXY *\/<br \/>\nstatic int rtm_to_ipmr_mfcc(struct net *net, struct nlmsghdr *nlh,<br \/>\n    struct mfcctl *mfcc, int *mrtsock,<br \/>\n    struct mr_table **mrtret,<br \/>\n    struct netlink_ext_ack *extack)<br \/>\n{<br \/>\nstruct net_device *dev &#061; NULL;<br \/>\nu32 tblid &#061; RT_TABLE_DEFAULT;<br \/>\nstruct mr_table *mrt;<br \/>\nstruct nlattr *attr;<br \/>\nstruct rtmsg *rtm;<br \/>\nint ret, rem;<\/p>\n<p>ret &#061; nlmsg_validate(nlh, sizeof(*rtm), RTA_MAX, rtm_ipmr_policy,<br \/>\n     extack);<br \/>\nif (ret &lt; 0)<br \/>\ngoto out;<br \/>\nrtm &#061; nlmsg_data(nlh);<\/p>\n<p>ret &#061; -EINVAL;<br \/>\nif (rtm-&gt;rtm_family !&#061; RTNL_FAMILY_IPMR || rtm-&gt;rtm_dst_len !&#061; 32 ||<br \/>\n    rtm-&gt;rtm_type !&#061; RTN_MULTICAST ||<br \/>\n    rtm-&gt;rtm_scope !&#061; RT_SCOPE_UNIVERSE ||<br \/>\n    !ipmr_rtm_validate_proto(rtm-&gt;rtm_protocol))<br \/>\ngoto out;<\/p>\n<p>memset(mfcc, 0, sizeof(*mfcc));<br \/>\nmfcc-&gt;mfcc_parent &#061; -1;<br \/>\nret &#061; 0;<br \/>\nnlmsg_for_each_attr(attr, nlh, sizeof(struct rtmsg), rem) {<br \/>\nswitch (nla_type(attr)) {<br \/>\ncase RTA_SRC:<br \/>\nmfcc-&gt;mfcc_origin.s_addr &#061; nla_get_be32(attr);<br \/>\nbreak;<br \/>\ncase RTA_DST:<br \/>\nmfcc-&gt;mfcc_mcastgrp.s_addr &#061; nla_get_be32(attr);<br \/>\nbreak;<br \/>\ncase RTA_IIF:<br \/>\ndev &#061; __dev_get_by_index(net, nla_get_u32(attr));<br \/>\nif (!dev) {<br \/>\nret &#061; -ENODEV;<br \/>\ngoto out;<br \/>\n}<br \/>\nbreak;<br \/>\ncase RTA_MULTIPATH:<br \/>\nif (ipmr_nla_get_ttls(attr, mfcc) &lt; 0) {<br \/>\nret &#061; -EINVAL;<br \/>\ngoto out;<br \/>\n}<br \/>\nbreak;<br \/>\ncase RTA_PREFSRC:<br \/>\nret &#061; 1;<br \/>\nbreak;<br \/>\ncase RTA_TABLE:<br \/>\ntblid &#061; nla_get_u32(attr);<br \/>\nbreak;<br \/>\n}<br \/>\n}<br \/>\nmrt &#061; ipmr_get_table(net, tblid);<br \/>\nif (!mrt) {<br \/>\nret &#061; -ENOENT;<br \/>\ngoto out;<br \/>\n}<br \/>\n*mrtret &#061; mrt;<br \/>\n*mrtsock &#061; rtm-&gt;rtm_protocol &#061;&#061; RTPROT_MROUTED ? 1 : 0;<br \/>\nif (dev)<br \/>\nmfcc-&gt;mfcc_parent &#061; ipmr_find_vif(mrt, dev);<\/p>\n<p>out:<br \/>\nreturn ret;<br \/>\n}<\/p>\n<p>\/* takes care of both newroute and delroute *\/<br \/>\nstatic int ipmr_rtm_route(struct sk_buff *skb, struct nlmsghdr *nlh,<br \/>\n  struct netlink_ext_ack *extack)<br \/>\n{<br \/>\nstruct net *net &#061; sock_net(skb-&gt;sk);<br \/>\nint ret, mrtsock, parent;<br \/>\nstruct mr_table *tbl;<br \/>\nstruct mfcctl mfcc;<\/p>\n<p>mrtsock &#061; 0;<br \/>\ntbl &#061; NULL;<br \/>\nret &#061; rtm_to_ipmr_mfcc(net, nlh, &amp;mfcc, &amp;mrtsock, &amp;tbl, extack);<br \/>\nif (ret &lt; 0)<br \/>\nreturn ret;<\/p>\n<p>parent &#061; ret ? mfcc.mfcc_parent : -1;<br \/>\nif (nlh-&gt;nlmsg_type &#061;&#061; RTM_NEWROUTE)<br \/>\nreturn ipmr_mfc_add(net, tbl, &amp;mfcc, mrtsock, parent);<br \/>\nelse<br \/>\nreturn ipmr_mfc_delete(tbl, &amp;mfcc, parent);<br \/>\n}<\/p>\n<p>static bool ipmr_fill_table(struct mr_table *mrt, struct sk_buff *skb)<br \/>\n{<br \/>\nu32 queue_len &#061; atomic_read(&amp;mrt-&gt;cache_resolve_queue_len);<\/p>\n<p>if (nla_put_u32(skb, IPMRA_TABLE_ID, mrt-&gt;id) ||<br \/>\n    nla_put_u32(skb, IPMRA_TABLE_CACHE_RES_QUEUE_LEN, queue_len) ||<br \/>\n    nla_put_s32(skb, IPMRA_TABLE_MROUTE_REG_VIF_NUM,<br \/>\nmrt-&gt;mroute_reg_vif_num) ||<br \/>\n    nla_put_u8(skb, IPMRA_TABLE_MROUTE_DO_ASSERT,<br \/>\n       mrt-&gt;mroute_do_assert) ||<br \/>\n    nla_put_u8(skb, IPMRA_TABLE_MROUTE_DO_PIM, mrt-&gt;mroute_do_pim) ||<br \/>\n    nla_put_u8(skb, IPMRA_TABLE_MROUTE_DO_WRVIFWHOLE,<br \/>\n       mrt-&gt;mroute_do_wrvifwhole))<br \/>\nreturn false;<\/p>\n<p>return true;<br \/>\n}<\/p>\n<p>static bool ipmr_fill_vif(struct mr_table *mrt, u32 vifid, struct sk_buff *skb)<br \/>\n{<br \/>\nstruct nlattr *vif_nest;<br \/>\nstruct vif_device *vif;<\/p>\n<p>\/* if the VIF doesn&#039;t exist just continue *\/<br \/>\nif (!VIF_EXISTS(mrt, vifid))<br \/>\nreturn true;<\/p>\n<p>vif &#061; &amp;mrt-&gt;vif_table[vifid];<br \/>\nvif_nest &#061; nla_nest_start(skb, IPMRA_VIF);<br \/>\nif (!vif_nest)<br \/>\nreturn false;<br \/>\nif (nla_put_u32(skb, IPMRA_VIFA_IFINDEX, vif-&gt;dev-&gt;ifindex) ||<br \/>\n    nla_put_u32(skb, IPMRA_VIFA_VIF_ID, vifid) ||<br \/>\n    nla_put_u16(skb, IPMRA_VIFA_FLAGS, vif-&gt;flags) ||<br \/>\n    nla_put_u64_64bit(skb, IPMRA_VIFA_BYTES_IN, vif-&gt;bytes_in,<br \/>\n      IPMRA_VIFA_PAD) ||<br \/>\n    nla_put_u64_64bit(skb, IPMRA_VIFA_BYTES_OUT, vif-&gt;bytes_out,<br \/>\n      IPMRA_VIFA_PAD) ||<br \/>\n    nla_put_u64_64bit(skb, IPMRA_VIFA_PACKETS_IN, vif-&gt;pkt_in,<br \/>\n      IPMRA_VIFA_PAD) ||<br \/>\n    nla_put_u64_64bit(skb, IPMRA_VIFA_PACKETS_OUT, vif-&gt;pkt_out,<br \/>\n      IPMRA_VIFA_PAD) ||<br \/>\n    nla_put_be32(skb, IPMRA_VIFA_LOCAL_ADDR, vif-&gt;local) ||<br \/>\n    nla_put_be32(skb, IPMRA_VIFA_REMOTE_ADDR, vif-&gt;remote)) {<br \/>\nnla_nest_cancel(skb, vif_nest);<br \/>\nreturn false;<br \/>\n}<br \/>\nnla_nest_end(skb, vif_nest);<\/p>\n<p>return true;<br \/>\n}<\/p>\n<p>static int ipmr_rtm_dumplink(struct sk_buff *skb, struct netlink_callback *cb)<br \/>\n{<br \/>\nstruct net *net &#061; sock_net(skb-&gt;sk);<br \/>\nstruct nlmsghdr *nlh &#061; NULL;<br \/>\nunsigned int t &#061; 0, s_t;<br \/>\nunsigned int e &#061; 0, s_e;<br \/>\nstruct mr_table *mrt;<\/p>\n<p>s_t &#061; cb-&gt;args[0];<br \/>\ns_e &#061; cb-&gt;args[1];<\/p>\n<p>ipmr_for_each_table(mrt, net) {<br \/>\nstruct nlattr *vifs, *af;<br \/>\nstruct ifinfomsg *hdr;<br \/>\nu32 i;<\/p>\n<p>if (t &lt; s_t)<br \/>\ngoto skip_table;<br \/>\nnlh &#061; nlmsg_put(skb, NETLINK_CB(cb-&gt;skb).portid,<br \/>\ncb-&gt;nlh-&gt;nlmsg_seq, RTM_NEWLINK,<br \/>\nsizeof(*hdr), NLM_F_MULTI);<br \/>\nif (!nlh)<br \/>\nbreak;<\/p>\n<p>hdr &#061; nlmsg_data(nlh);<br \/>\nmemset(hdr, 0, sizeof(*hdr));<br \/>\nhdr-&gt;ifi_family &#061; RTNL_FAMILY_IPMR;<\/p>\n<p>af &#061; nla_nest_start(skb, IFLA_AF_SPEC);<br \/>\nif (!af) {<br \/>\nnlmsg_cancel(skb, nlh);<br \/>\ngoto out;<br \/>\n}<\/p>\n<p>if (!ipmr_fill_table(mrt, skb)) {<br \/>\nnlmsg_cancel(skb, nlh);<br \/>\ngoto out;<br \/>\n}<\/p>\n<p>vifs &#061; nla_nest_start(skb, IPMRA_TABLE_VIFS);<br \/>\nif (!vifs) {<br \/>\nnla_nest_end(skb, af);<br \/>\nnlmsg_end(skb, nlh);<br \/>\ngoto out;<br \/>\n}<br \/>\nfor (i &#061; 0; i &lt; mrt-&gt;maxvif; i&#043;&#043;) {<br \/>\nif (e &lt; s_e)<br \/>\ngoto skip_entry;<br \/>\nif (!ipmr_fill_vif(mrt, i, skb)) {<br \/>\nnla_nest_end(skb, vifs);<br \/>\nnla_nest_end(skb, af);<br \/>\nnlmsg_end(skb, nlh);<br \/>\ngoto out;<br \/>\n}<br \/>\nskip_entry:<br \/>\ne&#043;&#043;;<br \/>\n}<br \/>\ns_e &#061; 0;<br \/>\ne &#061; 0;<br \/>\nnla_nest_end(skb, vifs);<br \/>\nnla_nest_end(skb, af);<br \/>\nnlmsg_end(skb, nlh);<br \/>\nskip_table:<br \/>\nt&#043;&#043;;<br \/>\n}<\/p>\n<p>out:<br \/>\ncb-&gt;args[1] &#061; e;<br \/>\ncb-&gt;args[0] &#061; t;<\/p>\n<p>return skb-&gt;len;<br \/>\n}<\/p>\n<p>#ifdef CONFIG_PROC_FS<br \/>\n\/* The \/proc interfaces to multicast routing :<br \/>\n * \/proc\/net\/ip_mr_cache &amp; \/proc\/net\/ip_mr_vif<br \/>\n *\/<\/p>\n<p>static void *ipmr_vif_seq_start(struct seq_file *seq, loff_t *pos)<br \/>\n__acquires(mrt_lock)<br \/>\n{<br \/>\nstruct mr_vif_iter *iter &#061; seq-&gt;private;<br \/>\nstruct net *net &#061; seq_file_net(seq);<br \/>\nstruct mr_table *mrt;<\/p>\n<p>mrt &#061; ipmr_get_table(net, RT_TABLE_DEFAULT);<br \/>\nif (!mrt)<br \/>\nreturn ERR_PTR(-ENOENT);<\/p>\n<p>iter-&gt;mrt &#061; mrt;<\/p>\n<p>read_lock(&amp;mrt_lock);<br \/>\nreturn mr_vif_seq_start(seq, pos);<br \/>\n}<\/p>\n<p>static void ipmr_vif_seq_stop(struct seq_file *seq, void *v)<br \/>\n__releases(mrt_lock)<br \/>\n{<br \/>\nread_unlock(&amp;mrt_lock);<br \/>\n}<\/p>\n<p>static int ipmr_vif_seq_show(struct seq_file *seq, void *v)<br \/>\n{<br \/>\nstruct mr_vif_iter *iter &#061; seq-&gt;private;<br \/>\nstruct mr_table *mrt &#061; iter-&gt;mrt;<\/p>\n<p>if (v &#061;&#061; SEQ_START_TOKEN) {<br \/>\nseq_puts(seq,<br \/>\n &#034;Interface      BytesIn  PktsIn  BytesOut PktsOut Flags Local    Remote\\\\n&#034;);<br \/>\n} else {<br \/>\nconst struct vif_device *vif &#061; v;<br \/>\nconst char *name &#061;  vif-&gt;dev ?<br \/>\n    vif-&gt;dev-&gt;name : &#034;none&#034;;<\/p>\n<p>seq_printf(seq,<br \/>\n   &#034;%2td %-10s %8ld %7ld  %8ld %7ld %05X %08X %08X\\\\n&#034;,<br \/>\n   vif &#8211; mrt-&gt;vif_table,<br \/>\n   name, vif-&gt;bytes_in, vif-&gt;pkt_in,<br \/>\n   vif-&gt;bytes_out, vif-&gt;pkt_out,<br \/>\n   vif-&gt;flags, vif-&gt;local, vif-&gt;remote);<br \/>\n}<br \/>\nreturn 0;<br \/>\n}<\/p>\n<p>static const struct seq_operations ipmr_vif_seq_ops &#061; {<br \/>\n.start &#061; ipmr_vif_seq_start,<br \/>\n.next  &#061; mr_vif_seq_next,<br \/>\n.stop  &#061; ipmr_vif_seq_stop,<br \/>\n.show  &#061; ipmr_vif_seq_show,<br \/>\n};<\/p>\n<p>static void *ipmr_mfc_seq_start(struct seq_file *seq, loff_t *pos)<br \/>\n{<br \/>\nstruct net *net &#061; seq_file_net(seq);<br \/>\nstruct mr_table *mrt;<\/p>\n<p>mrt &#061; ipmr_get_table(net, RT_TABLE_DEFAULT);<br \/>\nif (!mrt)<br \/>\nreturn ERR_PTR(-ENOENT);<\/p>\n<p>return mr_mfc_seq_start(seq, pos, mrt, &amp;mfc_unres_lock);<br \/>\n}<\/p>\n<p>static int ipmr_mfc_seq_show(struct seq_file *seq, void *v)<br \/>\n{<br \/>\nint n;<\/p>\n<p>if (v &#061;&#061; SEQ_START_TOKEN) {<br \/>\nseq_puts(seq,<br \/>\n &#034;Group    Origin   Iif     Pkts    Bytes    Wrong Oifs\\\\n&#034;);<br \/>\n} else {<br \/>\nconst struct mfc_cache *mfc &#061; v;<br \/>\nconst struct mr_mfc_iter *it &#061; seq-&gt;private;<br \/>\nconst struct mr_table *mrt &#061; it-&gt;mrt;<\/p>\n<p>seq_printf(seq, &#034;%08X %08X %-3hd&#034;,<br \/>\n   (__force u32) mfc-&gt;mfc_mcastgrp,<br \/>\n   (__force u32) mfc-&gt;mfc_origin,<br \/>\n   mfc-&gt;_c.mfc_parent);<\/p>\n<p>if (it-&gt;cache !&#061; &amp;mrt-&gt;mfc_unres_queue) {<br \/>\nseq_printf(seq, &#034; %8lu %8lu %8lu&#034;,<br \/>\n   mfc-&gt;_c.mfc_un.res.pkt,<br \/>\n   mfc-&gt;_c.mfc_un.res.bytes,<br \/>\n   mfc-&gt;_c.mfc_un.res.wrong_if);<br \/>\nfor (n &#061; mfc-&gt;_c.mfc_un.res.minvif;<br \/>\n     n &lt; mfc-&gt;_c.mfc_un.res.maxvif; n&#043;&#043;) {<br \/>\nif (VIF_EXISTS(mrt, n) &amp;&amp;<br \/>\n    mfc-&gt;_c.mfc_un.res.ttls[n] &lt; 255)<br \/>\nseq_printf(seq,<br \/>\n   &#034; %2d:%-3d&#034;,<br \/>\n   n, mfc-&gt;_c.mfc_un.res.ttls[n]);<br \/>\n}<br \/>\n} else {<br \/>\n\/* unresolved mfc_caches don&#039;t contain<br \/>\n * pkt, bytes and wrong_if values<br \/>\n *\/<br \/>\nseq_printf(seq, &#034; %8lu %8lu %8lu&#034;, 0ul, 0ul, 0ul);<br \/>\n}<br \/>\nseq_putc(seq, &#039;\\\\n&#039;);<br \/>\n}<br \/>\nreturn 0;<br \/>\n}<\/p>\n<p>static const struct seq_operations ipmr_mfc_seq_ops &#061; {<br \/>\n.start &#061; ipmr_mfc_seq_start,<br \/>\n.next  &#061; mr_mfc_seq_next,<br \/>\n.stop  &#061; mr_mfc_seq_stop,<br \/>\n.show  &#061; ipmr_mfc_seq_show,<br \/>\n};<br \/>\n#endif<\/p>\n<p>#ifdef CONFIG_IP_PIMSM_V2<br \/>\nstatic const struct net_protocol pim_protocol &#061; {<br \/>\n.handler&#061;pim_rcv,<br \/>\n.netns_ok&#061;1,<br \/>\n};<br \/>\n#endif<\/p>\n<p>static unsigned int ipmr_seq_read(struct net *net)<br \/>\n{<br \/>\nASSERT_RTNL();<\/p>\n<p>return net-&gt;ipv4.ipmr_seq &#043; ipmr_rules_seq_read(net);<br \/>\n}<\/p>\n<p>static int ipmr_dump(struct net *net, struct notifier_block *nb)<br \/>\n{<br \/>\nreturn mr_dump(net, nb, RTNL_FAMILY_IPMR, ipmr_rules_dump,<br \/>\n       ipmr_mr_table_iter, &amp;mrt_lock);<br \/>\n}<\/p>\n<p>static const struct fib_notifier_ops ipmr_notifier_ops_template &#061; {<br \/>\n.family&#061; RTNL_FAMILY_IPMR,<br \/>\n.fib_seq_read&#061; ipmr_seq_read,<br \/>\n.fib_dump&#061; ipmr_dump,<br \/>\n.owner&#061; THIS_MODULE,<br \/>\n};<\/p>\n<p>static int __net_init ipmr_notifier_init(struct net *net)<br \/>\n{<br \/>\nstruct fib_notifier_ops *ops;<\/p>\n<p>net-&gt;ipv4.ipmr_seq &#061; 0;<\/p>\n<p>ops &#061; fib_notifier_ops_register(&amp;ipmr_notifier_ops_template, net);<br \/>\nif (IS_ERR(ops))<br \/>\nreturn PTR_ERR(ops);<br \/>\nnet-&gt;ipv4.ipmr_notifier_ops &#061; ops;<\/p>\n<p>return 0;<br \/>\n}<\/p>\n<p>static void __net_exit ipmr_notifier_exit(struct net *net)<br \/>\n{<br \/>\nfib_notifier_ops_unregister(net-&gt;ipv4.ipmr_notifier_ops);<br \/>\nnet-&gt;ipv4.ipmr_notifier_ops &#061; NULL;<br \/>\n}<\/p>\n<p>\/* Setup for IP multicast routing *\/<br \/>\nstatic int __net_init ipmr_net_init(struct net *net)<br \/>\n{<br \/>\nint err;<\/p>\n<p>err &#061; ipmr_notifier_init(net);<br \/>\nif (err)<br \/>\ngoto ipmr_notifier_fail;<\/p>\n<p>err &#061; ipmr_rules_init(net);<br \/>\nif (err &lt; 0)<br \/>\ngoto ipmr_rules_fail;<\/p>\n<p>#ifdef CONFIG_PROC_FS<br \/>\nerr &#061; -ENOMEM;<br \/>\nif (!proc_create_net(&#034;ip_mr_vif&#034;, 0, net-&gt;proc_net, &amp;ipmr_vif_seq_ops,<br \/>\nsizeof(struct mr_vif_iter)))<br \/>\ngoto proc_vif_fail;<br \/>\nif (!proc_create_net(&#034;ip_mr_cache&#034;, 0, net-&gt;proc_net, &amp;ipmr_mfc_seq_ops,<br \/>\nsizeof(struct mr_mfc_iter)))<br \/>\ngoto proc_cache_fail;<br \/>\n#endif<br \/>\nreturn 0;<\/p>\n<p>#ifdef CONFIG_PROC_FS<br \/>\nproc_cache_fail:<br \/>\nremove_proc_entry(&#034;ip_mr_vif&#034;, net-&gt;proc_net);<br \/>\nproc_vif_fail:<br \/>\nipmr_rules_exit(net);<br \/>\n#endif<br \/>\nipmr_rules_fail:<br \/>\nipmr_notifier_exit(net);<br \/>\nipmr_notifier_fail:<br \/>\nreturn err;<br \/>\n}<\/p>\n<p>static void __net_exit ipmr_net_exit(struct net *net)<br \/>\n{<br \/>\n#ifdef CONFIG_PROC_FS<br \/>\nremove_proc_entry(&#034;ip_mr_cache&#034;, net-&gt;proc_net);<br \/>\nremove_proc_entry(&#034;ip_mr_vif&#034;, net-&gt;proc_net);<br \/>\n#endif<br \/>\nipmr_notifier_exit(net);<br \/>\nipmr_rules_exit(net);<br \/>\n}<\/p>\n<p>static struct pernet_operations ipmr_net_ops &#061; {<br \/>\n.init &#061; ipmr_net_init,<br \/>\n.exit &#061; ipmr_net_exit,<br \/>\n};<\/p>\n<p>int __init ip_mr_init(void)<br \/>\n{<br \/>\nint err;<\/p>\n<p>mrt_cachep &#061; kmem_cache_create(&#034;ip_mrt_cache&#034;,<br \/>\n       sizeof(struct mfc_cache),<br \/>\n       0, SLAB_HWCACHE_ALIGN | SLAB_PANIC,<br \/>\n       NULL);<\/p>\n<p>err &#061; register_pernet_subsys(&amp;ipmr_net_ops);<br \/>\nif (err)<br \/>\ngoto reg_pernet_fail;<\/p>\n<p>err &#061; register_netdevice_notifier(&amp;ip_mr_notifier);<br \/>\nif (err)<br \/>\ngoto reg_notif_fail;<br \/>\n#ifdef CONFIG_IP_PIMSM_V2<br \/>\nif (inet_add_protocol(&amp;pim_protocol, IPPROTO_PIM) &lt; 0) {<br \/>\npr_err(&#034;%s: can&#039;t add PIM protocol\\\\n&#034;, __func__);<br \/>\nerr &#061; -EAGAIN;<br \/>\ngoto add_proto_fail;<br \/>\n}<br \/>\n#endif<br \/>\nrtnl_register(RTNL_FAMILY_IPMR, RTM_GETROUTE,<br \/>\n      ipmr_rtm_getroute, ipmr_rtm_dumproute, 0);<br \/>\nrtnl_register(RTNL_FAMILY_IPMR, RTM_NEWROUTE,<br \/>\n      ipmr_rtm_route, NULL, 0);<br \/>\nrtnl_register(RTNL_FAMILY_IPMR, RTM_DELROUTE,<br \/>\n      ipmr_rtm_route, NULL, 0);<\/p>\n<p>rtnl_register(RTNL_FAMILY_IPMR, RTM_GETLINK,<br \/>\n      NULL, ipmr_rtm_dumplink, 0);<br \/>\nreturn 0;<\/p>\n<p>#ifdef CONFIG_IP_PIMSM_V2<br \/>\nadd_proto_fail:<br \/>\nunregister_netdevice_notifier(&amp;ip_mr_notifier);<br \/>\n#endif<br \/>\nreg_notif_fail:<br \/>\nunregister_pernet_subsys(&amp;ipmr_net_ops);<br \/>\nreg_pernet_fail:<br \/>\nkmem_cache_destroy(mrt_cachep);<br \/>\nreturn err;<br \/>\n}<\/p>\n<p>net\/ipv4\/ipmr.c\u00a0\u662f Linux \u5185\u6838\u4e2d\u5b9e\u73b0 IPv4 \u591a\u64ad\u8def\u7531\u7684\u6838\u5fc3\u6587\u4ef6&#xff0c;\u4e3b\u8981\u652f\u6301\u00a0mrouted\u00a0\u548c PIM&#xff08;Protocol Independent Multicast&#xff09;\u534f\u8bae\u3002\u4ee5\u4e0b\u662f\u5bf9\u5176\u5173\u952e\u90e8\u5206\u7684\u8be6\u7ec6\u5206\u6790&#xff1a;<\/p>\n<hr \/>\n<h4>1. \u6838\u5fc3\u6570\u636e\u7ed3\u6784<\/h4>\n<h5>a) \u591a\u64ad\u8def\u7531\u8868 (struct mr_table)<\/h5>\n<p>\u7ba1\u7406\u591a\u64ad\u8def\u7531\u4fe1\u606f&#xff0c;\u5305\u542b&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u865a\u62df\u63a5\u53e3\u8868 (vif_table): \u5b58\u50a8\u6240\u6709\u865a\u62df\u63a5\u53e3&#xff08;VIF&#xff09;\u4fe1\u606f\u3002<\/p>\n<\/li>\n<li>\n<p>\u591a\u64ad\u8f6c\u53d1\u7f13\u5b58 (MFC): \u4f7f\u7528\u54c8\u5e0c\u8868 (mfc_hash) \u5b58\u50a8\u5df2\u89e3\u6790\u7684\u8def\u7531\u6761\u76ee\u3002<\/p>\n<\/li>\n<li>\n<p>\u672a\u89e3\u6790\u961f\u5217 (mfc_unres_queue): \u4e34\u65f6\u5b58\u653e\u5f85\u5904\u7406\u7684\u591a\u64ad\u5305\u3002<\/p>\n<\/li>\n<li>\n<p>\u5b9a\u65f6\u5668 (ipmr_expire_timer): \u6e05\u7406\u8d85\u65f6\u672a\u89e3\u6790\u7684\u6761\u76ee\u3002<\/p>\n<\/li>\n<\/ul>\n<h5>b) \u865a\u62df\u63a5\u53e3 (struct vif_device)<\/h5>\n<p>\u8868\u793a\u4e00\u4e2a\u591a\u64ad\u865a\u62df\u63a5\u53e3&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u8bbe\u5907\u6307\u9488 (dev): \u5173\u8054\u7684\u7f51\u7edc\u8bbe\u5907\u3002<\/p>\n<\/li>\n<li>\n<p>\u672c\u5730\/\u8fdc\u7a0b\u5730\u5740 (local,\u00a0remote): \u96a7\u9053\u7aef\u70b9\u5730\u5740\u3002<\/p>\n<\/li>\n<li>\n<p>\u7edf\u8ba1\u4fe1\u606f: \u6536\u53d1\u5305\u6570\u3001\u5b57\u8282\u6570\u7b49\u3002<\/p>\n<\/li>\n<li>\n<p>\u6807\u5fd7\u4f4d (flags):<br \/>\nVIFF_TUNNEL&#xff08;\u96a7\u9053\u63a5\u53e3&#xff09;\u3001VIFF_REGISTER&#xff08;\u6ce8\u518c\u5230\u7528\u6237\u6001&#xff09;\u3001VIFF_STATIC&#xff08;\u9759\u6001\u914d\u7f6e&#xff09;\u3002<\/p>\n<\/li>\n<\/ul>\n<h5>c) \u591a\u64ad\u8f6c\u53d1\u7f13\u5b58 (struct mfc_cache)<\/h5>\n<p>\u5b58\u50a8\u591a\u64ad\u8def\u7531\u6761\u76ee&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u6e90\u5730\u5740\/\u7ec4\u5730\u5740 (mfc_origin,\u00a0mfc_mcastgrp): \u6807\u8bc6\u6d41\u3002<\/p>\n<\/li>\n<li>\n<p>\u8f93\u5165\u63a5\u53e3 (mfc_parent): \u63a5\u6536\u6d41\u91cf\u7684 VIF\u3002<\/p>\n<\/li>\n<li>\n<p>\u8f93\u51fa\u63a5\u53e3 TTL \u6620\u5c04 (res.ttls): \u6bcf\u4e2a VIF \u7684 TTL \u9608\u503c\u3002<\/p>\n<\/li>\n<li>\n<p>\u7edf\u8ba1\u4fe1\u606f: \u8f6c\u53d1\u5305\u6570\u3001\u5b57\u8282\u6570\u3001\u9519\u8bef\u8ba1\u6570\u3002<\/p>\n<\/li>\n<\/ul>\n<hr \/>\n<h4>2. \u6838\u5fc3\u529f\u80fd\u5b9e\u73b0<\/h4>\n<h5>a) \u865a\u62df\u63a5\u53e3\u7ba1\u7406<\/h5>\n<ul>\n<li>\n<p>\u521b\u5efa (vif_add)<br \/>\n\u652f\u6301\u4e09\u79cd\u7c7b\u578b\u63a5\u53e3&#xff1a;<\/p>\n<ul>\n<li>\n<p>VIFF_REGISTER: \u6ce8\u518c\u5230\u7528\u6237\u6001\u5b88\u62a4\u8fdb\u7a0b&#xff08;\u5982\u00a0pimd&#xff09;\u3002<\/p>\n<\/li>\n<li>\n<p>VIFF_TUNNEL: \u521b\u5efa IP-in-IP \u96a7\u9053&#xff08;\u5982 DVMRP&#xff09;\u3002<\/p>\n<\/li>\n<li>\n<p>\u7269\u7406\u63a5\u53e3: \u7ed1\u5b9a\u5230\u5b9e\u9645\u7f51\u7edc\u8bbe\u5907\u3002<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p>\u5220\u9664 (vif_delete)<br \/>\n\u89e3\u9664\u8bbe\u5907\u7ed1\u5b9a&#xff0c;\u66f4\u65b0\u7edf\u8ba1&#xff0c;\u6e05\u7406\u5173\u8054\u8d44\u6e90\u3002<\/p>\n<\/li>\n<\/ul>\n<h5>b) MFC \u6761\u76ee\u7ba1\u7406<\/h5>\n<ul>\n<li>\n<p>\u52a8\u6001\u5b66\u4e60 (ipmr_cache_find)<br \/>\n\u901a\u8fc7\u54c8\u5e0c\u8868\u5feb\u901f\u67e5\u627e MFC \u6761\u76ee\u3002<\/p>\n<\/li>\n<li>\n<p>\u9759\u6001\u914d\u7f6e (ipmr_mfc_add)<br \/>\n\u901a\u8fc7 Netlink \u6216 Socket \u6dfb\u52a0\u9759\u6001\u8def\u7531\u3002<\/p>\n<\/li>\n<li>\n<p>\u8d85\u65f6\u5904\u7406 (ipmr_expire_process)<br \/>\n\u6e05\u7406\u672a\u89e3\u6790\u7684 MFC \u6761\u76ee&#xff08;\u9ed8\u8ba4 10 \u79d2\u8d85\u65f6&#xff09;\u3002<\/p>\n<\/li>\n<\/ul>\n<h5>c) \u6570\u636e\u5305\u8f6c\u53d1 (ip_mr_forward)<\/h5>\n<p>\u5904\u7406\u6d41\u7a0b&#xff1a;<\/p>\n<li>\n<p>\u67e5\u627e MFC: \u5339\u914d\u6e90\u5730\u5740\u548c\u7ec4\u5730\u5740\u3002<\/p>\n<\/li>\n<li>\n<p>\u9a8c\u8bc1\u8f93\u5165\u63a5\u53e3: \u68c0\u67e5\u5305\u662f\u5426\u4ece\u6b63\u786e VIF \u8fdb\u5165\u3002<\/p>\n<\/li>\n<li>\n<p>TTL \u68c0\u67e5: \u8df3\u8fc7 TTL \u4e0d\u8db3\u7684\u51fa\u53e3\u3002<\/p>\n<\/li>\n<li>\n<p>\u514b\u9686\u5305\u591a\u64ad\u8f6c\u53d1:<\/p>\n<ul>\n<li>\n<p>\u96a7\u9053\u63a5\u53e3&#xff1a;\u5c01\u88c5 IP-in-IP \u5934\u3002<\/p>\n<\/li>\n<li>\n<p>\u7269\u7406\u63a5\u53e3&#xff1a;\u76f4\u63a5\u53d1\u9001\u3002<\/p>\n<\/li>\n<li>\n<p>\u6ce8\u518c\u63a5\u53e3&#xff1a;\u63d0\u4ea4\u7ed9\u7528\u6237\u6001\u5b88\u62a4\u8fdb\u7a0b\u3002<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<h5>d) \u672a\u89e3\u6790\u5305\u5904\u7406 (ipmr_cache_unresolved)<\/h5>\n<p>\u5f53 MFC \u4e0d\u5b58\u5728\u65f6&#xff1a;<\/p>\n<li>\n<p>\u521b\u5efa\u4e34\u65f6 MFC \u6761\u76ee\u5e76\u52a0\u5165\u672a\u89e3\u6790\u961f\u5217\u3002<\/p>\n<\/li>\n<li>\n<p>\u53d1\u9001\u00a0IGMPMSG_NOCACHE\u00a0\u901a\u77e5\u7528\u6237\u6001\u3002<\/p>\n<\/li>\n<li>\n<p>\u6536\u5230\u54cd\u5e94\u540e\u89e3\u6790\u6761\u76ee\u5e76\u8f6c\u53d1\u7f13\u5b58\u7684\u5305\u3002<\/p>\n<\/li>\n<hr \/>\n<h4>3. \u5173\u952e\u534f\u8bae\u652f\u6301<\/h4>\n<h5>a) PIM \u534f\u8bae\u5904\u7406<\/h5>\n<ul>\n<li>\n<p>PIMv1 (pim_rcv_v1)<br \/>\n\u5904\u7406 PIM \u6ce8\u518c\u6d88\u606f&#xff0c;\u5c01\u88c5\u5305\u8f6c\u53d1\u5230 RP\u3002<\/p>\n<\/li>\n<li>\n<p>PIMv2 (pim_rcv)<br \/>\n\u6821\u9a8c\u6d88\u606f\u6709\u6548\u6027&#xff0c;\u8f6c\u53d1\u5230\u6ce8\u518c\u63a5\u53e3\u3002<\/p>\n<\/li>\n<\/ul>\n<h5>b) IGMP \u6d88\u606f<\/h5>\n<ul>\n<li>\n<p>\u7f13\u5b58\u62a5\u544a (ipmr_cache_report)<br \/>\n\u53d1\u9001\u00a0IGMPMSG_WHOLEPKT\u00a0\u901a\u77e5\u7528\u6237\u6001\u65b0\u6d41\u91cf\u3002<\/p>\n<\/li>\n<\/ul>\n<hr \/>\n<h4>4. \u63a7\u5236\u5e73\u9762\u63a5\u53e3<\/h4>\n<h5>a) Netlink \u64cd\u4f5c<\/h5>\n<ul>\n<li>\n<p>\u8def\u7531\u64cd\u4f5c (ipmr_rtm_route)<br \/>\n\u5904\u7406\u00a0RTM_NEWROUTE\/RTM_DELROUTE\u00a0\u6dfb\u52a0\/\u5220\u9664 MFC\u3002<\/p>\n<\/li>\n<li>\n<p>\u4fe1\u606f\u67e5\u8be2 (ipmr_rtm_dumplink)<br \/>\n\u901a\u8fc7\u00a0RTM_GETLINK\u00a0\u8fd4\u56de VIF \u548c MFC \u8868\u4fe1\u606f\u3002<\/p>\n<\/li>\n<\/ul>\n<h5>b) Sysctl \/ Procfs<\/h5>\n<ul>\n<li>\n<p>\/proc\/net\/ip_mr_vif<br \/>\n\u663e\u793a\u6240\u6709 VIF \u7684\u7edf\u8ba1\u4fe1\u606f\u3002<\/p>\n<\/li>\n<li>\n<p>\/proc\/net\/ip_mr_cache<br \/>\n\u5217\u51fa MFC \u6761\u76ee&#xff0c;\u5305\u62ec\u6e90\u7ec4\u5730\u5740\u3001\u8f6c\u53d1\u8ba1\u6570\u3002<\/p>\n<\/li>\n<\/ul>\n<h5>c) Socket IOCTL<\/h5>\n<ul>\n<li>\n<p>SIOCGETVIFCNT<br \/>\n\u83b7\u53d6\u6307\u5b9a VIF \u7684\u6d41\u91cf\u7edf\u8ba1\u3002<\/p>\n<\/li>\n<li>\n<p>SIOCGETSGCNT<br \/>\n\u67e5\u8be2\u7279\u5b9a\u591a\u64ad\u6d41\u7684\u8f6c\u53d1\u7edf\u8ba1\u3002<\/p>\n<\/li>\n<\/ul>\n<hr \/>\n<h4>5. \u521d\u59cb\u5316\u548c\u6e05\u7406<\/h4>\n<ul>\n<li>\n<p>\u521d\u59cb\u5316 (ip_mr_init)<\/p>\n<li>\n<p>\u6ce8\u518c\u7f51\u7edc\u5b50\u7cfb\u7edf (register_pernet_subsys)\u3002<\/p>\n<\/li>\n<li>\n<p>\u521b\u5efa MFC \u7f13\u5b58\u5185\u5b58\u6c60 (kmem_cache_create)\u3002<\/p>\n<\/li>\n<li>\n<p>\u6ce8\u518c Netlink \u64cd\u4f5c\u548c PIM \u534f\u8bae\u5904\u7406\u5668\u3002<\/p>\n<\/li>\n<li>\n<p>\u6302\u8f7d Procfs \u6587\u4ef6\u3002<\/p>\n<\/li>\n<\/li>\n<li>\n<p>\u9000\u51fa\u6e05\u7406<br \/>\n\u91ca\u653e\u6240\u6709\u8def\u7531\u8868\u3001\u5b9a\u65f6\u5668\u3001Procfs \u9879\u3002<\/p>\n<\/li>\n<\/ul>\n<hr \/>\n<h4>6. \u8bbe\u8ba1\u4eae\u70b9<\/h4>\n<ul>\n<li>\n<p>\u591a\u8868\u652f\u6301 (CONFIG_IP_MROUTE_MULTIPLE_TABLES)<br \/>\n\u5141\u8bb8\u521b\u5efa\u591a\u4e2a\u72ec\u7acb\u7684\u591a\u64ad\u8def\u7531\u8868\u3002<\/p>\n<\/li>\n<li>\n<p>\u9ad8\u6548\u67e5\u627e<br \/>\n\u4f7f\u7528 RCU \u4fdd\u62a4\u7684\u54c8\u5e0c\u8868\u7ba1\u7406 MFC\u3002<\/p>\n<\/li>\n<li>\n<p>\u7528\u6237\u6001\u534f\u4f5c<br \/>\n\u901a\u8fc7\u539f\u59cb\u5957\u63a5\u5b57 (mroute_sk) \u4e0e\u00a0mrouted\/pimd\u00a0\u4ea4\u4e92\u3002<\/p>\n<\/li>\n<li>\n<p>\u6a21\u5757\u5316\u7ed3\u6784<br \/>\n\u5206\u79bb\u8f6c\u53d1\/\u63a7\u5236\u903b\u8f91&#xff0c;\u652f\u6301\u52a8\u6001\u6269\u5c55\u3002<\/p>\n<\/li>\n<\/ul>\n<hr \/>\n<h4>\u603b\u7ed3<\/h4>\n<p>ipmr.c\u00a0\u5b9e\u73b0\u4e86\u5b8c\u6574\u7684 IPv4 \u591a\u64ad\u8def\u7531\u6846\u67b6&#xff0c;\u6838\u5fc3\u529f\u80fd\u5305\u62ec&#xff1a;<\/p>\n<li>\n<p>\u865a\u62df\u63a5\u53e3\u7ba1\u7406&#xff1a;\u652f\u6301\u7269\u7406\u63a5\u53e3\u3001\u96a7\u9053\u548c\u6ce8\u518c\u63a5\u53e3\u3002<\/p>\n<\/li>\n<li>\n<p>\u8f6c\u53d1\u5f15\u64ce&#xff1a;\u57fa\u4e8e MFC \u6761\u76ee\u9ad8\u6548\u514b\u9686\u8f6c\u53d1\u5305\u3002<\/p>\n<\/li>\n<li>\n<p>\u534f\u8bae\u652f\u6301&#xff1a;\u4e0e PIM\/DVMRP \u5b88\u62a4\u8fdb\u7a0b\u534f\u540c\u5de5\u4f5c\u3002<\/p>\n<\/li>\n<li>\n<p>\u63a7\u5236\u63a5\u53e3&#xff1a;\u63d0\u4f9b Netlink\u3001IOCTL\u3001Procfs \u7b49\u7ba1\u7406\u65b9\u5f0f\u3002<br \/>\n\u5176\u8bbe\u8ba1\u5e73\u8861\u4e86\u6027\u80fd\u548c\u7075\u6d3b\u6027&#xff0c;\u662f Linux \u591a\u64ad\u8def\u7531\u7684\u57fa\u77f3\u3002<\/p>\n<\/li>\n","protected":false},"excerpt":{"rendered":"<p>\u6587\u7ae0\u6d4f\u89c8\u9605\u8bfb227\u6b21\u3002\u672c\u6587\u6df1\u5165\u5256\u6790\u4e86Linux\u5185\u6838\u4e2dIPv4\u591a\u64ad\u8def\u7531\u7684\u6838\u5fc3\u5b9e\u73b0\u673a\u5236\uff0c\u4e3b\u8981\u5305\u542b\u4ee5\u4e0b\u5173\u952e\u5185\u5bb9\uff1a \u6838\u5fc3\u6570\u636e\u7ed3\u6784 \u591a\u64ad\u8def\u7531\u8868\uff08mr_table\uff09\uff1a\u7ba1\u7406\u865a\u62df\u63a5\u53e3\u8868\u3001\u8f6c\u53d1\u7f13\u5b58\u548c\u672a\u89e3\u6790\u961f\u5217 \u865a\u62df\u63a5\u53e3\uff08vif_device\uff09\uff1a\u652f\u6301\u7269\u7406\u3001\u96a7\u9053\u548c\u6ce8\u518c\u4e09\u79cd\u63a5\u53e3\u7c7b\u578b \u8f6c\u53d1\u7f13\u5b58\uff08mfc_cache\uff09\uff1a\u5b58\u50a8\u6e90\/\u7ec4\u5730\u5740\u3001\u8f93\u5165\u63a5\u53e3\u548c\u8f93\u51faTTL\u6620\u5c04 \u6570\u636e\u5305\u8f6c\u53d1\u6d41\u7a0b \u5feb\u901f\u5339\u914d\uff1a\u54c8\u5e0c\u8868\u67e5\u627eMFC\u6761\u76ee TTL\u6821\u9a8c\uff1a\u8fc7\u6ee4\u65e0\u6548\u51fa\u53e3\u63a5\u53e3 \u667a\u80fd\u514b\u9686\uff1a\u4ec5\u590d\u5236\u5305\u5934\u51cf\u5c11\u5f00\u9500 \u591a\u63a5\u53e3\u8f6c\u53d1\uff1a\u652f\u6301\u96a7\u9053\u5c01\u88c5\u548c\u76f4\u63a5\u53d1\u9001 \u63a7\u5236\u5e73\u9762\u8bbe\u8ba1 \u52a8\u6001\u5b66\u4e60\uff1a\u901a\u8fc7PIM\/IGMP\u6d88\u606f\u81ea\u52a8\u521b\u5efa<\/p>\n","protected":false},"author":2,"featured_media":46958,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[174,1084,50,78],"topic":[],"class_list":["post-46960","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-server","tag-c","tag-linux","tag-50","tag-78"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v20.3 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Linux\u5185\u6838IPv4\u591a\u64ad\u8def\u7531\u6df1\u5ea6\u89e3\u6790\uff1a\u4ece\u6570\u636e\u7ed3\u6784\u5230\u9ad8\u6548\u8f6c\u53d1 - \u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.wsisp.com\/helps\/46960.html\" \/>\n<meta property=\"og:locale\" content=\"zh_CN\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Linux\u5185\u6838IPv4\u591a\u64ad\u8def\u7531\u6df1\u5ea6\u89e3\u6790\uff1a\u4ece\u6570\u636e\u7ed3\u6784\u5230\u9ad8\u6548\u8f6c\u53d1 - \u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3\" \/>\n<meta property=\"og:description\" content=\"\u6587\u7ae0\u6d4f\u89c8\u9605\u8bfb227\u6b21\u3002\u672c\u6587\u6df1\u5165\u5256\u6790\u4e86Linux\u5185\u6838\u4e2dIPv4\u591a\u64ad\u8def\u7531\u7684\u6838\u5fc3\u5b9e\u73b0\u673a\u5236\uff0c\u4e3b\u8981\u5305\u542b\u4ee5\u4e0b\u5173\u952e\u5185\u5bb9\uff1a \u6838\u5fc3\u6570\u636e\u7ed3\u6784 \u591a\u64ad\u8def\u7531\u8868\uff08mr_table\uff09\uff1a\u7ba1\u7406\u865a\u62df\u63a5\u53e3\u8868\u3001\u8f6c\u53d1\u7f13\u5b58\u548c\u672a\u89e3\u6790\u961f\u5217 \u865a\u62df\u63a5\u53e3\uff08vif_device\uff09\uff1a\u652f\u6301\u7269\u7406\u3001\u96a7\u9053\u548c\u6ce8\u518c\u4e09\u79cd\u63a5\u53e3\u7c7b\u578b \u8f6c\u53d1\u7f13\u5b58\uff08mfc_cache\uff09\uff1a\u5b58\u50a8\u6e90\/\u7ec4\u5730\u5740\u3001\u8f93\u5165\u63a5\u53e3\u548c\u8f93\u51faTTL\u6620\u5c04 \u6570\u636e\u5305\u8f6c\u53d1\u6d41\u7a0b \u5feb\u901f\u5339\u914d\uff1a\u54c8\u5e0c\u8868\u67e5\u627eMFC\u6761\u76ee TTL\u6821\u9a8c\uff1a\u8fc7\u6ee4\u65e0\u6548\u51fa\u53e3\u63a5\u53e3 \u667a\u80fd\u514b\u9686\uff1a\u4ec5\u590d\u5236\u5305\u5934\u51cf\u5c11\u5f00\u9500 \u591a\u63a5\u53e3\u8f6c\u53d1\uff1a\u652f\u6301\u96a7\u9053\u5c01\u88c5\u548c\u76f4\u63a5\u53d1\u9001 \u63a7\u5236\u5e73\u9762\u8bbe\u8ba1 \u52a8\u6001\u5b66\u4e60\uff1a\u901a\u8fc7PIM\/IGMP\u6d88\u606f\u81ea\u52a8\u521b\u5efa\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.wsisp.com\/helps\/46960.html\" \/>\n<meta property=\"og:site_name\" content=\"\u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3\" \/>\n<meta property=\"article:published_time\" content=\"2025-07-29T20:57:35+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.wsisp.com\/helps\/wp-content\/uploads\/2025\/07\/20250729205726-688935b62982b.png\" \/>\n<meta name=\"author\" content=\"admin\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"\u4f5c\u8005\" \/>\n\t<meta name=\"twitter:data1\" content=\"admin\" \/>\n\t<meta name=\"twitter:label2\" content=\"\u9884\u8ba1\u9605\u8bfb\u65f6\u95f4\" \/>\n\t<meta name=\"twitter:data2\" content=\"66 \u5206\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.wsisp.com\/helps\/46960.html\",\"url\":\"https:\/\/www.wsisp.com\/helps\/46960.html\",\"name\":\"Linux\u5185\u6838IPv4\u591a\u64ad\u8def\u7531\u6df1\u5ea6\u89e3\u6790\uff1a\u4ece\u6570\u636e\u7ed3\u6784\u5230\u9ad8\u6548\u8f6c\u53d1 - \u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3\",\"isPartOf\":{\"@id\":\"https:\/\/www.wsisp.com\/helps\/#website\"},\"datePublished\":\"2025-07-29T20:57:35+00:00\",\"dateModified\":\"2025-07-29T20:57:35+00:00\",\"author\":{\"@id\":\"https:\/\/www.wsisp.com\/helps\/#\/schema\/person\/358e386c577a3ab51c4493330a20ad41\"},\"breadcrumb\":{\"@id\":\"https:\/\/www.wsisp.com\/helps\/46960.html#breadcrumb\"},\"inLanguage\":\"zh-Hans\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.wsisp.com\/helps\/46960.html\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.wsisp.com\/helps\/46960.html#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"\u9996\u9875\",\"item\":\"https:\/\/www.wsisp.com\/helps\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Linux\u5185\u6838IPv4\u591a\u64ad\u8def\u7531\u6df1\u5ea6\u89e3\u6790\uff1a\u4ece\u6570\u636e\u7ed3\u6784\u5230\u9ad8\u6548\u8f6c\u53d1\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.wsisp.com\/helps\/#website\",\"url\":\"https:\/\/www.wsisp.com\/helps\/\",\"name\":\"\u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3\",\"description\":\"\u9999\u6e2f\u670d\u52a1\u5668_\u9999\u6e2f\u4e91\u670d\u52a1\u5668\u8d44\u8baf_\u670d\u52a1\u5668\u5e2e\u52a9\u6587\u6863_\u670d\u52a1\u5668\u6559\u7a0b\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.wsisp.com\/helps\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"zh-Hans\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.wsisp.com\/helps\/#\/schema\/person\/358e386c577a3ab51c4493330a20ad41\",\"name\":\"admin\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"zh-Hans\",\"@id\":\"https:\/\/www.wsisp.com\/helps\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/gravatar.wp-china-yes.net\/avatar\/?s=96&d=mystery\",\"contentUrl\":\"https:\/\/gravatar.wp-china-yes.net\/avatar\/?s=96&d=mystery\",\"caption\":\"admin\"},\"sameAs\":[\"http:\/\/wp.wsisp.com\"],\"url\":\"https:\/\/www.wsisp.com\/helps\/author\/admin\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Linux\u5185\u6838IPv4\u591a\u64ad\u8def\u7531\u6df1\u5ea6\u89e3\u6790\uff1a\u4ece\u6570\u636e\u7ed3\u6784\u5230\u9ad8\u6548\u8f6c\u53d1 - \u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.wsisp.com\/helps\/46960.html","og_locale":"zh_CN","og_type":"article","og_title":"Linux\u5185\u6838IPv4\u591a\u64ad\u8def\u7531\u6df1\u5ea6\u89e3\u6790\uff1a\u4ece\u6570\u636e\u7ed3\u6784\u5230\u9ad8\u6548\u8f6c\u53d1 - \u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3","og_description":"\u6587\u7ae0\u6d4f\u89c8\u9605\u8bfb227\u6b21\u3002\u672c\u6587\u6df1\u5165\u5256\u6790\u4e86Linux\u5185\u6838\u4e2dIPv4\u591a\u64ad\u8def\u7531\u7684\u6838\u5fc3\u5b9e\u73b0\u673a\u5236\uff0c\u4e3b\u8981\u5305\u542b\u4ee5\u4e0b\u5173\u952e\u5185\u5bb9\uff1a \u6838\u5fc3\u6570\u636e\u7ed3\u6784 \u591a\u64ad\u8def\u7531\u8868\uff08mr_table\uff09\uff1a\u7ba1\u7406\u865a\u62df\u63a5\u53e3\u8868\u3001\u8f6c\u53d1\u7f13\u5b58\u548c\u672a\u89e3\u6790\u961f\u5217 \u865a\u62df\u63a5\u53e3\uff08vif_device\uff09\uff1a\u652f\u6301\u7269\u7406\u3001\u96a7\u9053\u548c\u6ce8\u518c\u4e09\u79cd\u63a5\u53e3\u7c7b\u578b \u8f6c\u53d1\u7f13\u5b58\uff08mfc_cache\uff09\uff1a\u5b58\u50a8\u6e90\/\u7ec4\u5730\u5740\u3001\u8f93\u5165\u63a5\u53e3\u548c\u8f93\u51faTTL\u6620\u5c04 \u6570\u636e\u5305\u8f6c\u53d1\u6d41\u7a0b \u5feb\u901f\u5339\u914d\uff1a\u54c8\u5e0c\u8868\u67e5\u627eMFC\u6761\u76ee TTL\u6821\u9a8c\uff1a\u8fc7\u6ee4\u65e0\u6548\u51fa\u53e3\u63a5\u53e3 \u667a\u80fd\u514b\u9686\uff1a\u4ec5\u590d\u5236\u5305\u5934\u51cf\u5c11\u5f00\u9500 \u591a\u63a5\u53e3\u8f6c\u53d1\uff1a\u652f\u6301\u96a7\u9053\u5c01\u88c5\u548c\u76f4\u63a5\u53d1\u9001 \u63a7\u5236\u5e73\u9762\u8bbe\u8ba1 \u52a8\u6001\u5b66\u4e60\uff1a\u901a\u8fc7PIM\/IGMP\u6d88\u606f\u81ea\u52a8\u521b\u5efa","og_url":"https:\/\/www.wsisp.com\/helps\/46960.html","og_site_name":"\u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3","article_published_time":"2025-07-29T20:57:35+00:00","og_image":[{"url":"https:\/\/www.wsisp.com\/helps\/wp-content\/uploads\/2025\/07\/20250729205726-688935b62982b.png"}],"author":"admin","twitter_card":"summary_large_image","twitter_misc":{"\u4f5c\u8005":"admin","\u9884\u8ba1\u9605\u8bfb\u65f6\u95f4":"66 \u5206"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.wsisp.com\/helps\/46960.html","url":"https:\/\/www.wsisp.com\/helps\/46960.html","name":"Linux\u5185\u6838IPv4\u591a\u64ad\u8def\u7531\u6df1\u5ea6\u89e3\u6790\uff1a\u4ece\u6570\u636e\u7ed3\u6784\u5230\u9ad8\u6548\u8f6c\u53d1 - \u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3","isPartOf":{"@id":"https:\/\/www.wsisp.com\/helps\/#website"},"datePublished":"2025-07-29T20:57:35+00:00","dateModified":"2025-07-29T20:57:35+00:00","author":{"@id":"https:\/\/www.wsisp.com\/helps\/#\/schema\/person\/358e386c577a3ab51c4493330a20ad41"},"breadcrumb":{"@id":"https:\/\/www.wsisp.com\/helps\/46960.html#breadcrumb"},"inLanguage":"zh-Hans","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.wsisp.com\/helps\/46960.html"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.wsisp.com\/helps\/46960.html#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"\u9996\u9875","item":"https:\/\/www.wsisp.com\/helps"},{"@type":"ListItem","position":2,"name":"Linux\u5185\u6838IPv4\u591a\u64ad\u8def\u7531\u6df1\u5ea6\u89e3\u6790\uff1a\u4ece\u6570\u636e\u7ed3\u6784\u5230\u9ad8\u6548\u8f6c\u53d1"}]},{"@type":"WebSite","@id":"https:\/\/www.wsisp.com\/helps\/#website","url":"https:\/\/www.wsisp.com\/helps\/","name":"\u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3","description":"\u9999\u6e2f\u670d\u52a1\u5668_\u9999\u6e2f\u4e91\u670d\u52a1\u5668\u8d44\u8baf_\u670d\u52a1\u5668\u5e2e\u52a9\u6587\u6863_\u670d\u52a1\u5668\u6559\u7a0b","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.wsisp.com\/helps\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"zh-Hans"},{"@type":"Person","@id":"https:\/\/www.wsisp.com\/helps\/#\/schema\/person\/358e386c577a3ab51c4493330a20ad41","name":"admin","image":{"@type":"ImageObject","inLanguage":"zh-Hans","@id":"https:\/\/www.wsisp.com\/helps\/#\/schema\/person\/image\/","url":"https:\/\/gravatar.wp-china-yes.net\/avatar\/?s=96&d=mystery","contentUrl":"https:\/\/gravatar.wp-china-yes.net\/avatar\/?s=96&d=mystery","caption":"admin"},"sameAs":["http:\/\/wp.wsisp.com"],"url":"https:\/\/www.wsisp.com\/helps\/author\/admin"}]}},"_links":{"self":[{"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/posts\/46960","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/comments?post=46960"}],"version-history":[{"count":0,"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/posts\/46960\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/media\/46958"}],"wp:attachment":[{"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/media?parent=46960"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/categories?post=46960"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/tags?post=46960"},{"taxonomy":"topic","embeddable":true,"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/topic?post=46960"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}