사설망을 위한 서버 구축이 필요해서 IPTime PX 2500 을 구매했다. PCI-E x1 을 지원해서 구매했다.

Windows 에만 드라이버가 있고 리눅스에는 없는듯..! -_-;

검색해보니, 사용하는 칩셋이 Realtek 사의 RTL 8125 이다.

(RTL 8125를 쓴다는 것도 왜 때문인지 정보가 너무 빈약하다. 데이터시트나 그런게 없고 참고 정보로 있음..)

http://prod.danawa.com/info/?pcode=9372108
http://www.newstap.co.kr/news/articleView.html?idxno=95772

구형 컴퓨터여서 Ubuntu 20.04 가 정상적으로 동작하지 않아서 CentOS 7 을 설치해놨더니 여기에는 r8125 모듈이 없어서 그런가 NIC가 인식이 안된다.

현재 상태 점검

PCI 에 꼽았으니 먼저 lspci 로 살펴본다.

$ sudo yum install -y pciutils  # lspci 가 없는 경우 설치
$ sudo lspci
...
00:1f.0 ISA bridge: Intel Corporation Z97 Chipset LPC Controller
00:1f.2 SATA controller: Intel Corporation 9 Series Chipset Family SATA Controller [AHCI Mode]
00:1f.3 SMBus: Intel Corporation 9 Series Chipset Family SMBus Controller
03:00.0 Ethernet controller: Realtek Semiconductor Co., Ltd. RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (rev 0c)
05:00.0 Ethernet controller: Realtek Semiconductor Co., Ltd. RTL8125 2.5GbE Controller

03:00.0 은 기존에 있던 NIC 이고 05:00.0 은 새로 꼽은 NIC 인 것으로 확인된다.

$ sudo modprobe r8125
modprobe: FATAL: Module r8125 not found.

r8125 자체가 리눅스 커널 6부터 들어갔다는 모양인데, CentOS 7 의 최신 버전의 커널은 3.10 이니… 당연히 없음…!


시스템 상에서 지원하는 NIC 종류 확인

자신의 운영체제 내에 지원하는 NIC의 종류를 알아보려면 다음의 디렉토리를 살펴보면 된다.

$ ls -alh /lib/modules/$(uname -r)/kernel/drivers/net/ethernet/

total 48K
drwxr-xr-x. 29 root root 4.0K Sep 10 03:36 .
drwxr-xr-x. 16 root root 4.0K Sep 10 03:36 ..
drwxr-xr-x.  3 root root   17 Sep 10 03:36 amazon
drwxr-xr-x.  3 root root   61 Sep 10 03:36 amd
drwxr-xr-x.  3 root root   22 Sep 10 03:36 aquantia
drwxr-xr-x.  6 root root   55 Sep 10 03:36 atheros
drwxr-xr-x.  4 root root  101 Sep 10 03:36 broadcom
drwxr-xr-x.  3 root root   17 Sep 10 03:36 brocade
drwxr-xr-x.  2 root root   48 Sep 10 03:36 cadence
drwxr-xr-x.  2 root root   25 Sep 10 03:36 calxeda
drwxr-xr-x.  3 root root   22 Sep 10 03:36 cavium
drwxr-xr-x.  6 root root   62 Sep 10 03:36 chelsio
drwxr-xr-x.  3 root root   18 Sep 10 03:36 cisco
drwxr-xr-x.  3 root root   19 Sep 10 03:36 dec
-rw-r--r--.  1 root root 8.4K Sep  8 00:02 dnet.ko.xz
drwxr-xr-x.  3 root root   19 Sep 10 03:36 emulex
-rw-r--r--.  1 root root 7.8K Sep  8 00:02 ethoc.ko.xz
drwxr-xr-x.  3 root root   17 Sep 10 03:36 google
drwxr-xr-x.  2 root root   23 Sep 10 03:36 icplus
drwxr-xr-x. 13 root root  144 Sep 10 03:36 intel
-rw-r--r--.  1 root root  19K Sep  8 00:02 jme.ko.xz
drwxr-xr-x.  2 root root   62 Sep 10 03:36 marvell
drwxr-xr-x.  6 root root   56 Sep 10 03:36 mellanox
drwxr-xr-x.  3 root root   22 Sep 10 03:36 myricom
drwxr-xr-x.  3 root root   17 Sep 10 03:36 netronome
drwxr-xr-x.  3 root root   21 Sep 10 03:36 oki-semi
drwxr-xr-x.  7 root root   90 Sep 10 03:36 qlogic
drwxr-xr-x.  2 root root   82 Sep 14 21:28 realtek
drwxr-xr-x.  2 root root   26 Sep 10 03:36 rocker
drwxr-xr-x.  3 root root   37 Sep 10 03:36 sfc
drwxr-xr-x.  2 root root   49 Sep 10 03:36 smsc
drwxr-xr-x.  2 root root   24 Sep 10 03:36 ti

드라이버 컴파일

드라이버를 컴파일 하자.

먼저 OS를 최신 버전으로 업데이트 해준다.

$ sudo yum update
$ sudo yum upgrade -y
$ sudo yum install -y epel-release

그리고 커널 소스 코드가 필요하다.

$ sudo yum install -y kernel-headers kernel-devel

그리고 드라이버 소스 코드. Realtek 홈페이지에서 받았다.

파일 링크

압축 받고 풀어준다.

$ tar -xvf r8125-9.006.04.tar.bz2

bz2 가 없어서 lbzip2 도 설치해주었음.

$ sudo yum install -y lbzip2

압축을 풀고 컴파일..!

$ cd r8125-9.006.04
$ ./autorun.sh

여기서… autorun.sh 를 하는 순간 네트워크가 끊긴다. 기존 네트워크 모듈을 제거하고 새로 설치하기 때문에, SSH 가 끊김.. =_=;; 그래서 이후로는 직접 모니터에 붙어서 작업했기 때문에 기억이 정확하지 않을 수 있음을 미리 말씀드린다.


드라이버 코드 수정

아무튼 직접 가서 보니 네트워크가 안된다. 뜨헉..

몇 가지… 드라이버 코드를 손봐야만 한다. 전부 적용이 안 된 듯…

1. 없는 필드 (1)

없는 필드가 나온다. src/r8125_n.c 파일. 11770 라인이다.

11770 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)
11771 static const struct net_device_ops rtl8125_netdev_ops = {
11772         .ndo_open       = rtl8125_open,
11773         .ndo_stop       = rtl8125_close,
11774         .ndo_get_stats      = rtl8125_get_stats,
11775         .ndo_start_xmit     = rtl8125_start_xmit,
11776         .ndo_tx_timeout     = rtl8125_tx_timeout,
11777         .ndo_change_mtu     = rtl8125_change_mtu,
11778         .ndo_set_mac_address    = rtl8125_set_mac_address,
11779         .ndo_do_ioctl       = rtl8125_do_ioctl,
11780 #if LINUX_VERSION_CODE < KERNEL_VERSION(3,1,0)
11781         .ndo_set_multicast_list = rtl8125_set_rx_mode,

net_device_ops 구조체 멤버에 ndo_change_mtu 필드가 없는데 사용하려고 한다는 것. 컴파일러에서 친절하게

혹시 멤버 이름이 ndo_change_mtu_rh74 아니에요?

라고 알려준다. 그래서 그냥 바꿔버렸다.

11770 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)
11771 static const struct net_device_ops rtl8125_netdev_ops = {
11772         .ndo_open       = rtl8125_open,
11773         .ndo_stop       = rtl8125_close,
11774         .ndo_get_stats      = rtl8125_get_stats,
11775         .ndo_start_xmit     = rtl8125_start_xmit,
11776         .ndo_tx_timeout     = rtl8125_tx_timeout,
11777         /*.ndo_change_mtu     = rtl8125_change_mtu,*/
11778         .ndo_change_mtu_rh74     = rtl8125_change_mtu,
11779         .ndo_set_mac_address    = rtl8125_set_mac_address,
11780         .ndo_do_ioctl       = rtl8125_do_ioctl,
11781 #if LINUX_VERSION_CODE < KERNEL_VERSION(3,1,0)
11782         .ndo_set_multicast_list = rtl8125_set_rx_mode,

2. 함수 중복 선언

ether_addr_copy 이 함수가 왜 때문에 중복 선언이 된다.

src/r8125.h 파일. 69 라인이다.

68 #if LINUX_VERSION_CODE < KERNEL_VERSION(3,14,0)
69 static inline void ether_addr_copy(u8 *dst, const u8 *src)
70 {
71         u16 *a = (u16 *)dst;
72         const u16 *b = (const u16 *)src;
73
74         a[0] = b[0];
75         a[1] = b[1];
76         a[2] = b[2];
77 }
78 #endif

근데 에러 메시지를 보니까 src/r8125_n.c 에서 include 한 <linux/etherdevice.h> 에 이미 선언이 되어 있는데 중복 선언 했다는 듯.

그래서 <linux/etherdevice.h> 를 보기 위해… /lib/modules/$(uname -r)/build/include/linux/etherdevice.h 를 살펴보았다.

239 /**
240  * ether_addr_copy - Copy an Ethernet address
241  * @dst: Pointer to a six-byte array Ethernet address destination
242  * @src: Pointer to a six-byte array Ethernet address source
243  *
244  * Please note: dst & src must both be aligned to u16.
245  */
246 static inline void ether_addr_copy(u8 *dst, const u8 *src)
247 {
248 #if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
249   *(u32 *)dst = *(const u32 *)src;
250   *(u16 *)(dst + 4) = *(const u16 *)(src + 4);
251 #else
252   u16 *a = (u16 *)dst;
253   const u16 *b = (const u16 *)src;
254
255   a[0] = b[0];
256   a[1] = b[1];
257   a[2] = b[2];
258 #endif
259 }

코드를 읽어보면

248 #if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
249   *(u32 *)dst = *(const u32 *)src;
250   *(u16 *)(dst + 4) = *(const u16 *)(src + 4);
251 #else

이 부분 빼고는 똑같다. 저 부분도 UNALIGNED_ACCESS 를 EFFICIENT 하게 하기 위한 설정 값이 있을때 도는 거니까 동일할 거라고 보자.

그래서 그냥 src/r8125.h 파일에 있는 선언을 주석처리 해버렸다.

68 #if LINUX_VERSION_CODE < KERNEL_VERSION(3,14,0)
69 static inline void ether_addr_copy(u8 *dst, const u8 *src)
70 /*{
71         u16 *a = (u16 *)dst;
72         const u16 *b = (const u16 *)src;
73
74         a[0] = b[0];
75         a[1] = b[1];
76         a[2] = b[2];
77 }*/
78 #endif

3. 없는 필드 (2)

없는 필드가 또 있다. net_device_ops 구조체에last_rx 필드를 사용한다는데, 얘는 비슷한 것도 없는지 컴파일러에서 안 알려준다.

src/r8125_n.c 파일. 14581 라인이다.

14577                         if (rtl8125_rx_vlan_skb(tp, desc, skb) < 0)
14578                                 rtl8125_rx_skb(tp, skb, ring_index);
14579
14580 #if LINUX_VERSION_CODE < KERNEL_VERSION(4,11,0)
14581                         dev->last_rx = jiffies;
14582 #endif //LINUX_VERSION_CODE < KERNEL_VERSION(4,11,0)
14583                         RTLDEV->stats.rx_bytes += pkt_size;
14584                         RTLDEV->stats.rx_packets++;
14585                 }
14586
14587                 cur_rx++;

검색을 좀 해봤다.

최신 커널(2017년 12월 기준 4.13)에서 last_rx 필드가 없다는 깃헙 이슈. https://github.com/freifunk-gluon/batman-adv-legacy/issues/10

위의 깃헙 이슈를 살펴보면, 당시 최신 커널인 4.13 등에서 last_rx 를 사용할 수 없다는 이슈가 있다.

더 이상 쓰지 않는 last_rx 구조체 멤버 필드를 없애자는 이슈. https://patchwork.ozlabs.org/project/intel-wired-lan/patch/20170118164501.1934-1-tklauser@distanz.ch/

위의 이슈를 살펴보면, 네트워크 스택에서 last_rx 는 일부 드라이버에서 (남)용하는 경우를 빼고는 더이상 사용하지 않는 것으로 보인다. 즉, 그냥 남겨두기는 하였으나 반드시 필요한 값은 아니라는 것.

(흥미로운건, 위의 이슈가 17년 1월의 이슈고… 깃헙 이슈는 17년 12월 이슈다. 즉, 17년 1월 즈음에 4.11 버전의 커널 패치가 이루어지며 상위 버전에 영향을 끼친 것.)

코드 상에서는 커널 버전 4.11 이하일 때 해당 필드를 사용하라는 조건부 컴파일이 들어가 있는데, 주변을 살펴보면 그렇다고 해서 커널 버전 4.11 이상일때 그를 위한 무언가 다른 처리가 없다. 즉, 없어도 그만이라는 뜻. 그래서 주석처리 하였다.

14577                         if (rtl8125_rx_vlan_skb(tp, desc, skb) < 0)
14578                                 rtl8125_rx_skb(tp, skb, ring_index);
14579
14580 /*#if LINUX_VERSION_CODE < KERNEL_VERSION(4,11,0) */
14581 /*                        dev->last_rx = jiffies; */
14582 /*#endif //LINUX_VERSION_CODE < KERNEL_VERSION(4,11,0) */
14583                         RTLDEV->stats.rx_bytes += pkt_size;
14584                         RTLDEV->stats.rx_packets++;
14585                 }
14586
14587                 cur_rx++;

컴파일

이제 다 수정하였으니, 다시 컴파일 해준다.

[root@localhost localhost]# make

CC:  cc
CCVERSION:  4.8.5
KERNEL_GCC_VERSION:  4.8.5
KVER:  3.10.0-1160.42.2.el7.x86_64
KMAJ:  3
KMIN:  10
KREV:  0
BASEDIR:  /lib/modules/3.10.0-1160.42.2.el7.x86_64
DRIVERDIR:  /lib/modules/3.10.0-1160.42.2.el7.x86_64/kernel/drivers/net/ethernet/realtek
PWD:  /root/r8125-9.006.04/src
RTKDIR:  kernel/drivers/net/ethernet/realtek

make -C /lib/modules/3.10.0-1160.42.2.el7.x86_64/build M=/root/r8125-9.006.04/src clean
make[1]: Entering directory `/usr/src/kernels/3.10.0-1160.42.2.el7.x86_64'
  CLEAN   /root/r8125-9.006.04/src/.tmp_versions
  CLEAN   /root/r8125-9.006.04/src/Module.symvers
make[1]: Leaving directory `/usr/src/kernels/3.10.0-1160.42.2.el7.x86_64'
make -C /lib/modules/3.10.0-1160.42.2.el7.x86_64/build M=/root/r8125-9.006.04/src modules
make[1]: Entering directory `/usr/src/kernels/3.10.0-1160.42.2.el7.x86_64'
  CC [M]  /root/r8125-9.006.04/src/r8125_n.o
/root/r8125-9.006.04/src/r8125_n.c: In function ‘rtl8125_poll_msix_other’:
/root/r8125-9.006.04/src/r8125_n.c:11928:22: warning: unused variable ‘work_to_do’ [-Wunused-variable]
         unsigned int work_to_do = RTL_NAPI_QUOTA(budget, dev);
                      ^
  CC [M]  /root/r8125-9.006.04/src/rtl_eeprom.o
  CC [M]  /root/r8125-9.006.04/src/rtltool.o
  LD [M]  /root/r8125-9.006.04/src/r8125.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /root/r8125-9.006.04/src/r8125.mod.o
  LD [M]  /root/r8125-9.006.04/src/r8125.ko
make[1]: Leaving directory `/usr/src/kernels/3.10.0-1160.42.2.el7.x86_64'
make -C /lib/modules/3.10.0-1160.42.2.el7.x86_64/build M=/root/r8125-9.006.04/src INSTALL_MOD_DIR=kernel/drivers/net/ethernet/realtek modules_install
make[1]: Entering directory `/usr/src/kernels/3.10.0-1160.42.2.el7.x86_64'
  INSTALL /root/r8125-9.006.04/src/r8125.ko
Can't read private key
  DEPMOD  3.10.0-1160.42.2.el7.x86_64
make[1]: Leaving directory `/usr/src/kernels/3.10.0-1160.42.2.el7.x86_64'

컴파일은 잘 되지만.. 두둥… ‘Can’t read private key’ 라는 새로운 에러가 발생하며 모듈을 어떻게 할 수가 없다.

Secure boot 모드에서는 서드파티 모듈 설치가 안 된다고 한다. 그래서 Secure boot 를 꺼주어야 함…

[root@localhost localhost]# dmesg | grep -i secure
[    0.000000] Secure boot enabled
[    0.481934] EFI: Loaded cert 'Hewlett-Packard Company: HP UEFI Secure Boot 2013 DB key: 1d7cf2c2b92673f69c8ee1ec7063967ab9b62bec' linked to '.system_keyring'
[    0.482786] EFI: Loaded cert 'CentOS Secure Boot CA 2: 70007f99209c126be14774eaec7b6d9631f34dca' linked to '.system_keyring'

BIOS 에 들어가서 꺼주고 나서 다시 모듈을 넣으면 된다.

[root@localhost localhost]# modprobe r8125

이후에는 새로운 NIC가 잘 인식되는 것을 알 수 있다.

[root@localhost localhost]# ip a s
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eno1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 12:34:56:78:90:ab brd ff:ff:ff:ff:ff:ff
    inet 101.22.33.44/24 brd 101.22.33.255 scope global noprefixroute eno1
       valid_lft forever preferred_lft forever
    inet6 fe80::1111:2222:3333:4444/64 scope link noprefixroute
       valid_lft forever preferred_lft forever
3: enp5s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 23:34:56:78:90:ab brd ff:ff:ff:ff:ff:ff
    inet 192.168.16.1/20 brd 192.168.31.255 scope global noprefixroute enp5s0
       valid_lft forever preferred_lft forever
    inet6 fe80::2222:3333:4444:5555/64 scope link
       valid_lft forever preferred_lft forever