2018年2月22日

ブロードバンドタワー國武です。

今回も小ネタです。

先日、通信影響を与えずに、Linuxに設定されたIPアドレスのネットマスクを変更したくなりました。

こんな時、使うのは当然 ip コマンドです。ナウでヤングな若者は ip コマンドを使うのです。

従来は、ip コマンドではなくて、ifconfig やら route コマンドを駆使して作業することが多かったかと思いますが、IPv4やらIPv6やらの設定をするには、書式が統一されている ip コマンドの方が実際、扱い易いです。

IP アドレス関連のパラメータ変更で利用するのは ip address もしくは ip addr です。引数を調べるには、これに help をつければOKです。

# ip addr help
Usage: ip addr {add|change|replace} IFADDR dev STRING [ LIFETIME ]
                                                      [ CONFFLAG-LIST ]
       ip addr del IFADDR dev STRING [mngtmpaddr]
       ip addr {show|save|flush} [ dev STRING ] [ scope SCOPE-ID ]
                            [ to PREFIX ] [ FLAG-LIST ] [ label PATTERN ] [up]
       ip addr {showdump|restore}
IFADDR := PREFIX | ADDR peer PREFIX
          [ broadcast ADDR ] [ anycast ADDR ]
          [ label STRING ] [ scope SCOPE-ID ]
SCOPE-ID := [ host | link | global | NUMBER ]
FLAG-LIST := [ FLAG-LIST ] FLAG
FLAG  := [ permanent | dynamic | secondary | primary |
           tentative | deprecated | dadfailed | temporary |
           CONFFLAG-LIST ]
CONFFLAG-LIST := [ CONFFLAG-LIST ] CONFFLAG
CONFFLAG  := [ home | nodad | mngtmpaddr | noprefixroute ]
LIFETIME := [ valid_lft LFT ] [ preferred_lft LFT ]
LFT := forever | SECONDS

ということで、いかにも ip addr change もしくは ip addr replace コマンドが使えそうな感じです。
ただ、change と replace になんの違いがあるか気になりますね。ip(8) やら ip-address(8) のマニュアルを読んでみましたが、なんと記述がない!
きっとマニュアルの更新が追いついてないんでしょう。最新のソースコードを見れば、きっと書いてるに違いありません。

https://www.kernel.org/pub/linux/utils/net/iproute2/

で、最新のソースコードに添付されているマニュアルを読むと……記述がないですね orz

仕方ないので、そのままソースコード(iproute2-4.15.0.tar.gzを取得し展開)を確認します。

  • ip/ipaddress.c
if (matches(*argv, "change") == 0 ||
        strcmp(*argv, "chg") == 0)
        return ipaddr_modify(RTM_NEWADDR, NLM_F_REPLACE, argc-1, argv+1);
if (matches(*argv, "replace") == 0)
        return ipaddr_modify(RTM_NEWADDR, NLM_F_CREATE|NLM_F_REPLACE, argc-1, argv+1);

change と replace には違いがありそうです。さらに

  • include/uapi/linux/netlink.h

を確認すると NLM_F_から始まる Flags の定義の後に

/* Modifiers to NEW request */
#define NLM_F_REPLACE 0x100 /* Override existing */
#define NLM_F_EXCL 0x200 /* Do not touch, if it exists */
#define NLM_F_CREATE 0x400 /* Create, if it does not exist */
#define NLM_F_APPEND 0x800 /* Add to end of list */

とあったので、ip addr change は、フラグの設定がなければ設定する、そうでなければ ip addr change も ip addr replace も上書きしてくれるようです。今回はフラグの操作ではないので、動作に違いはなさそうですが念のため動作確認していきます。

今回想定している環境は下記の通りです。

ネットマスク変更検証構成
図1. ネットマスク変更検証構成

右下の Linux に設定されている 10.0.2.15/24 を 10.0.2.15/23 に変更します。また、その変更作業によって通信影響が発生しないことを確認するため、作業対象のIPアドレス 10.0.2.15 宛に ssh でログインしてそのsshセッション上で変更していきます。もし問題が発生したら、応答がなくなる、セッションが切れるなどの影響が出るはずです(うまくいくかどうかわからない段階で、本当はこんなことしちゃダメなんですけどね)

さて、実際に sshでLinuxにログインして確認すると、設定されている IPアドレスはこんな感じです。

$ ip addr show dev eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 08:00:27:6b:57:88 brd ff:ff:ff:ff:ff:ff
    inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic eth0
       valid_lft 85884sec preferred_lft 85884sec
    inet6 fe80::a00:27ff:fe6b:5788/64 scope link
       valid_lft forever preferred_lft forever

経路はこんな感じです。

$ ip route show dev eth0
default via 10.0.2.2 dev eth0 proto static metric 100
10.0.2.0/24 dev eth0 proto kernel scope link src 10.0.2.15 metric 100

さて、実際に netmaskを変更していきます。ちなみにこの状態で、変更後のアドレスとなる 10.0.2.15/23 をそのままつけようとすると、下記のようなエラーが……

# ip addr add  10.0.2.15/23 brd 10.0.3.255 dev eth0
# ip addr show dev eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 08:00:27:6b:57:88 brd ff:ff:ff:ff:ff:ff
    inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic eth0
       valid_lft 85692sec preferred_lft 85692sec
    inet 10.0.2.15/23 brd 10.0.3.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::a00:27ff:fe6b:5788/64 scope link
       valid_lft forever preferred_lft forever
# ip route show dev eth0
default via 10.0.2.2 dev eth0  proto static  metric 100
10.0.2.0/24 dev eth0  proto kernel  scope link  src 10.0.2.15  metric 100
10.0.2.0/23 dev eth0  proto kernel  scope link  src 10.0.2.15

でない……普通に設定できるやないか!

  • 10.0.2.15/23
  • 10.0.2.15/24

の、両方がちゃんとついてますね。そのまま /24 のアドレスを削除すると……

# ip addr del 10.0.2.15/24 dev eth0
# ip addr show dev eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 08:00:27:6b:57:88 brd ff:ff:ff:ff:ff:ff
    inet 10.0.2.15/23 brd 10.0.3.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::a00:27ff:fe6b:5788/64 scope link
       valid_lft forever preferred_lft forever
# ip route show dev eth0
default via 10.0.2.2 dev eth0  proto static  metric 100
10.0.2.0/23 dev eth0  proto kernel  scope link  src 10.0.2.15

なんの問題もなく終わってしまった。change/replace を調べた意味がなかった……ちなみに ip addr change や ip addr replace でも試してみましたが、同様になんの問題もなく変更できました。結局フラグ操作しない限りは、add/change/replace は同じ挙動ってことですね。ip addr add だと

# ip addr add 10.0.2.15/23 dev eth0
RTNETLINK answers: File exists

こんなエラーが出るのだと思い込んでいました。改めてソースコードを眺めると

if (matches(*argv, "add") == 0)
        return ipaddr_modify(RTM_NEWADDR, NLM_F_CREATE|NLM_F_EXCL, argc-1, argv+1);
if (matches(*argv, "change") == 0 ||
        strcmp(*argv, "chg") == 0)
        return ipaddr_modify(RTM_NEWADDR, NLM_F_REPLACE, argc-1, argv+1);
if (matches(*argv, "replace") == 0)
        return ipaddr_modify(RTM_NEWADDR, NLM_F_CREATE|NLM_F_REPLACE, argc-1, argv+1);

add/change/replace ではアドレス設定の操作に違いはなさそうです。

ともあれ、今回の検証では変更にともなってセッションが切断されるなどの通信影響を出さずにマスク変更の拡張が可能であることが確認できました。
もちろん、再起動後は設定が戻ってしまうので、CentOS/RHEL であれば /etc/sysconfig/network-scripts/ifcfg-eth0 の修正などが必要になるかと思います。

目ざとい人は、もともとついてた 10.0.2.15/24 のアドレスにライフタイムが設定されていることから、上記はDHCPで付けられたIPアドレスに対する検証でしかなく、 static で付けているIPアドレスとでも同じ挙動になるのかが気になるかもしれません。念のため追試していますが、static でも DHCPで振られたテンポラリのアドレスでも、どちらも同じ手順でネットマスクの変更が可能でした。

ということで、今回得られた教訓は、「先入観怖い」というお話しでした……

本ブログの情報につきましては、自社の検証に基づいた結果からの情報提供であり、
品質保証を目的としたものではございません。

投稿者: Koichi KUNITAKE

最近は Ansible やら Sphinx 触ってます。IPv6 はボチボチ……