內(nèi)核參數(shù)ip_forward刨根問底

概述

在服務(wù)器上遏暴,我們一般會(huì)根據(jù)官方文檔或者遇到網(wǎng)絡(luò)不通的時(shí)候,去設(shè)置ip_forward設(shè)置為1指黎,問題雖然解決了朋凉,可能并不清楚ip_forward背后的原理,本篇文章將一探究竟醋安。

  • 為什么要設(shè)置ip_forward為1杂彭?
  • ip_forward在協(xié)議棧處理的哪個(gè)過程中生效?
  • 如何在繁雜的內(nèi)核代碼中找到處理ip_forward相關(guān)的流程吓揪。

ip_forward作用

一般來說亲怠,一臺(tái)服務(wù)器只處理mac地址和ip地址為本機(jī)的數(shù)據(jù)包,即ip_forward默認(rèn)為0柠辞。但是隨著云和虛擬網(wǎng)絡(luò)的發(fā)展赁炎,linux可以作為一個(gè)軟路由轉(zhuǎn)發(fā)路過的數(shù)據(jù)包。因此當(dāng)需要宿主機(jī)處理目的地址非本機(jī)的數(shù)據(jù)包時(shí),需要使能ip_forward徙垫。

// 臨時(shí)生效,永久生效放棒,需要修改/etc/sysctl.conf文件
echo 1 > /proc/sys/net/ipv4/ip_forward

ip_forward在哪個(gè)處理過程中生效

還是用經(jīng)典的netfilter的框架圖姻报。

image.png

這里只分析最簡(jiǎn)單的一種情況,單播间螟。下面這段代碼是路由查找過程中的一個(gè)函數(shù)吴旋,從上面的圖看出,在prerouting后厢破,要經(jīng)過router模塊荣瑟,去判斷該包的類型,是送往本機(jī)的包摩泪,還是需要轉(zhuǎn)發(fā)的包笆焰。注意這里的轉(zhuǎn)發(fā)可以是直接出物理網(wǎng)卡,也可以是轉(zhuǎn)發(fā)到主機(jī)的其他虛擬網(wǎng)卡這張圖是三層的處理见坑,能到prerouting進(jìn)行處理嚷掠,說明mac地址是已經(jīng)匹配了的。

static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr,
                   u8 tos, struct net_device *dev,
                   struct fib_result *res)
{
   struct in_device *in_dev = __in_dev_get_rcu(dev);
   // 查找路由表
   err = fib_lookup(net, &fl4, res, 0);
   // 本機(jī)的包荞驴,繼續(xù)處理
   if (res->type == RTN_LOCAL) {
        err = fib_validate_source(skb, saddr, daddr, tos,
                      0, dev, in_dev, &itag);
        if (err < 0)
            goto martian_source;
        goto local_input;
  }
  // 非本機(jī)的包不皆,如果ip_forward未使能,返回錯(cuò)誤
  if (!IN_DEV_FORWARD(in_dev)) {
        err = -EHOSTUNREACH;
        goto no_route;
   }
}

那這里如何從查找路由的結(jié)果中判斷是本機(jī)的包還是非本機(jī)的包熊楼?這就涉及到路由表的知識(shí)霹娄,常用的默認(rèn)存在的三張表 default,main鲫骗,local犬耻。fib_lookup會(huì)根據(jù)目的地址去查找,并將結(jié)果保存到res中挎峦。

// 查看local表的路由香追,目的地址屬于這里就是本機(jī)的包。
[root@10 yaml]# ip r list table local
broadcast 10.10.101.0 dev ens192 proto kernel scope link src 10.10.101.91 
local 10.10.101.91 dev ens192 proto kernel scope host src 10.10.101.91 
broadcast 10.10.101.255 dev ens192 proto kernel scope link src 10.10.101.91 
local 10.96.0.1 dev kube-ipvs0 proto kernel scope host src 10.96.0.1 
local 10.96.0.10 dev kube-ipvs0 proto kernel scope host src 10.96.0.10 
local 10.96.0.161 dev kube-ipvs0 proto kernel scope host src 10.96.0.161 
local 10.96.9.71 dev kube-ipvs0 proto kernel scope host src 10.96.9.71 
local 10.96.37.82 dev kube-ipvs0 proto kernel scope host src 10.96.37.82 
local 10.96.106.146 dev kube-ipvs0 proto kernel scope host src 10.96.106.146 
local 10.96.127.95 dev kube-ipvs0 proto kernel scope host src 10.96.127.95 
local 10.96.179.37 dev kube-ipvs0 proto kernel scope host src 10.96.179.37 
local 10.96.241.158 dev kube-ipvs0 proto kernel scope host src 10.96.241.158 
local 10.244.12.177 dev vxlan.calicoV4 proto kernel scope host src 10.244.12.177 
broadcast 127.0.0.0 dev lo proto kernel scope link src 127.0.0.1 
local 127.0.0.0/8 dev lo proto kernel scope host src 127.0.0.1 
local 127.0.0.1 dev lo proto kernel scope host src 127.0.0.1 
broadcast 127.255.255.255 dev lo proto kernel scope link src 127.0.0.1 
broadcast 172.17.0.0 dev docker0 proto kernel scope link src 172.17.0.1 
local 172.17.0.1 dev docker0 proto kernel scope host src 172.17.0.1 
broadcast 172.17.255.255 dev docker0 proto kernel scope link src 172.17.0.1 
// 保存路由表查找結(jié)果的數(shù)據(jù)結(jié)構(gòu)
struct fib_result {
    __be32      prefix;
    unsigned char   prefixlen;
    unsigned char   nh_sel;
    unsigned char   type;
    unsigned char   scope;
    u32     tclassid;
    struct fib_info *fi;
    struct fib_table *table;
    struct hlist_head *fa_head;
};

如何在代碼中查找ip_forward的處理流程

如果直接去翻看內(nèi)核代碼坦胶,根本是沒有頭緒的透典,下面的介紹也是閱讀的一種技巧。
1顿苇、 搜索ip_forward關(guān)鍵字峭咒,查看proc的處理函數(shù),及echo 1 > ip_forward后內(nèi)核的處理函數(shù)纪岁。
2凑队、 找到ip_forward設(shè)置的方式,一般會(huì)有宏處理。
3漩氨、 轉(zhuǎn)變思路西壮,查找ip_forward使用的地方。

// 通過ip_forward關(guān)鍵字叫惊,找到proc相關(guān)處理函數(shù)款青,即devinet_sysctl_forward
static struct ctl_table ctl_forward_entry[] = {
    {
        .procname   = "ip_forward",
        .data       = &ipv4_devconf.data[
                    IPV4_DEVCONF_FORWARDING - 1],
        .maxlen     = sizeof(int),
        .mode       = 0644,
        .proc_handler   = devinet_sysctl_forward,
        .extra1     = &ipv4_devconf,
        .extra2     = &init_net,
    },
};
static int devinet_sysctl_forward(struct ctl_table *ctl, int write,
                  void __user *buffer,
                  size_t *lenp, loff_t *ppos)
{
   // 這兩個(gè)函數(shù)就是關(guān)鍵點(diǎn),看函數(shù)名霍狰,就可以大概知道作用抡草。
    inet_forward_change(net);
    inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
                            NETCONFA_FORWARDING,
                            NETCONFA_IFINDEX_DEFAULT,
                            net->ipv4.devconf_dflt);
}
// 查看inet_forward_change函數(shù)
static void inet_forward_change(struct net *net)
{
     // 遍歷該命名空間的所有網(wǎng)卡,設(shè)置網(wǎng)卡屬性
    for_each_netdev(net, dev) {
        struct in_device *in_dev;
        in_dev = __in_dev_get_rtnl(dev);
        if (in_dev) {
            // 這里是重點(diǎn)蔗坯,需要探究這個(gè)宏將這個(gè)on保存到了哪里
            IN_DEV_CONF_SET(in_dev, FORWARDING, on);
            inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
                            NETCONFA_FORWARDING,
                            dev->ifindex, &in_dev->cnf);
        }
    }
}
// 查看IN_DEV_CONF_SET宏康震,清晰明了的看到這個(gè)on保存到了哪里。
#define IN_DEV_CONF_SET(in_dev, attr, val) \
    ipv4_devconf_set((in_dev), IPV4_DEVCONF_ ## attr, (val))
static inline void ipv4_devconf_set(struct in_device *in_dev, int index,
                    int val)
{
    set_bit(index, in_dev->cnf.state);
    in_dev->cnf.data[index] = val;
}

通過上面的分析宾濒,我們清楚的看到了ip_forward的處理流程以及保存在了哪個(gè)數(shù)據(jù)結(jié)構(gòu)中腿短。另外看到set(IN_DEV_CONF_SET)函數(shù),對(duì)應(yīng)就應(yīng)該有g(shù)et函數(shù)鼎兽,那么查找get函數(shù)就可以找到該參數(shù)在哪些流程中生效答姥。

#define IN_DEV_CONF_GET(in_dev, attr) \
    ipv4_devconf_get((in_dev), IPV4_DEVCONF_ ## attr)
// 在以下函數(shù)中都有調(diào)用
ip_route_input_mc
__mkroute_input
ip_route_input_slow  // 這個(gè)就是上面分析的函數(shù),只是一種情況谚咬。
__mkroute_output
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末鹦付,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子择卦,更是在濱河造成了極大的恐慌敲长,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,183評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件秉继,死亡現(xiàn)場(chǎng)離奇詭異祈噪,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)尚辑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門辑鲤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人杠茬,你說我怎么就攤上這事月褥。” “怎么了瓢喉?”我有些...
    開封第一講書人閱讀 168,766評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵宁赤,是天一觀的道長。 經(jīng)常有香客問我栓票,道長决左,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,854評(píng)論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮佛猛,結(jié)果婚禮上惑芭,老公的妹妹穿的比我還像新娘。我一直安慰自己继找,他們只是感情好强衡,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著码荔,像睡著了一般。 火紅的嫁衣襯著肌膚如雪感挥。 梳的紋絲不亂的頭發(fā)上缩搅,一...
    開封第一講書人閱讀 52,457評(píng)論 1 311
  • 那天,我揣著相機(jī)與錄音触幼,去河邊找鬼硼瓣。 笑死,一個(gè)胖子當(dāng)著我的面吹牛置谦,可吹牛的內(nèi)容都是我干的堂鲤。 我是一名探鬼主播,決...
    沈念sama閱讀 40,999評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼媒峡,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼瘟栖!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起谅阿,我...
    開封第一講書人閱讀 39,914評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤半哟,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后签餐,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體寓涨,經(jīng)...
    沈念sama閱讀 46,465評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評(píng)論 3 342
  • 正文 我和宋清朗相戀三年氯檐,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了戒良。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,675評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡冠摄,死狀恐怖糯崎,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情耗拓,我是刑警寧澤拇颅,帶...
    沈念sama閱讀 36,354評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站乔询,受9級(jí)特大地震影響樟插,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評(píng)論 3 335
  • 文/蒙蒙 一黄锤、第九天 我趴在偏房一處隱蔽的房頂上張望搪缨。 院中可真熱鬧,春花似錦鸵熟、人聲如沸副编。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽痹届。三九已至,卻和暖如春打月,著一層夾襖步出監(jiān)牢的瞬間队腐,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評(píng)論 1 274
  • 我被黑心中介騙來泰國打工奏篙, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留柴淘,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,091評(píng)論 3 378
  • 正文 我出身青樓秘通,卻偏偏與公主長得像为严,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子肺稀,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評(píng)論 2 360

推薦閱讀更多精彩內(nèi)容