Archivi tag: udev

Ubuntu: analisi e debug di un’interfaccia di rete malfunzionante

Di recente, analizzando i log di uno dei server che gestisco, mi sono accorto che una delle sue interfacce di rete continuava a generare tutta una serie di eventi del tipo:

root@linux-box:~$ dmesg | grep eth0
[  184.348573] 8139too 00:40:f4:44:68:8e: eth0: link up
[  190.271409] 8139too 00:40:f4:44:68:8e: eth0: link up
[  196.374125] 8139too 00:40:f4:44:68:8e: eth0: link up
[  201.823936] 8139too 00:40:f4:44:68:8e: eth0: link up
[  208.089862] 8139too 00:40:f4:44:68:8e: eth0: link up
[  212.200536] 8139too 00:40:f4:44:68:8e: eth0: link up
[  216.950829] 8139too 00:40:f4:44:68:8e: eth0: link up
[  221.071493] 8139too 00:40:f4:44:68:8e: eth0: link up
[  226.731134] 8139too 00:40:f4:44:68:8e: eth0: link up
[  230.778527] 8139too 00:40:f4:44:68:8e: eth0: link up
[  234.126353] 8139too 00:40:f4:44:68:8e: eth0: link up
[  238.936576] 8139too 00:40:f4:44:68:8e: eth0: link up
[  244.859427] 8139too 00:40:f4:44:68:8e: eth0: link up
[  250.845563] 8139too 00:40:f4:44:68:8e: eth0: link up
[  256.831661] 8139too 00:40:f4:44:68:8e: eth0: link up
[  262.458027] 8139too 00:40:f4:44:68:8e: eth0: link up
[  266.428794] 8139too 00:40:f4:44:68:8e: eth0: link up
[  271.305659] 8139too 00:40:f4:44:68:8e: eth0: link up
[  277.091885] 8139too 00:40:f4:44:68:8e: eth0: link up
[  282.768241] 8139too 00:40:f4:44:68:8e: eth0: link up
[  287.102103] 8139too 00:40:f4:44:68:8e: eth0: link up
[  290.866316] 8139too 00:40:f4:44:68:8e: eth0: link up
[  294.267496] 8139too 00:40:f4:44:68:8e: eth0: link up

ubuntu

In più, essa risultava completamente “sorda”, nel senso che anche sniffando il traffico mediante tcpdump, non vi era evidenza di frame/pacchetti di alcun genere, nonostante fosse collegata ad un segmento di rete piuttosto popolato.

Alla luce di ciò, le cause del suddetto malfunzionamento sarebbero potute essere 2:

1) guasto hardware;
2) aggiornamento che ha compromesso i driver del kernel che si interfacciano con la scheda (leggasi moduli), rendendola inutilizzabile.

Prima di procedere con l’analisi, però, è opportuno capire come avviene il riconoscimento dell’interfaccia di rete da parte del sistema operativo durante la fase di boot.

Chipset e moduli del kernel

Durante l’accesione (boot) della macchina, il sistema operativo riconosce la scheda di rete “dialongando” con il chipset di cui è dotata. Per individuarlo è sufficiente lanciare il seguente comando (con relativo output):

root@linux-box:~# lspci | grep Ethernet

04:01.0 Ethernet controller: Realtek Semiconductor Co., Ltd. RTL-8139/8139C/8139C+ (rev 10)

Nella fattispecie, il chipset utilizzato dalla scheda è Realtek.

Il modulo del kernel in grado di interfacciarsi con essa prende il nome di r8139too e deve essere caricato in fase di avvio. Per verificare che il suddetto modulo sia effettivamente up è sufficiente lanciare il comando:

root@linux-box:~$ lsmod | grep 8139

il cui output sarà simile al seguente:

8139too                32177  0
8139cp                 27360  0

Udev e nomenclatura

Se il modulo è stato correttamente caricato, la scheda di rete può essere effettivamente “riconosciuta” come tale ed il sistema operativo sarà in grado di assegnarle un nome.

Durante tale fase entra in gioco il demone udev, che “leggerà” il contenuto del file di configurazione dedicato alle interfacce di rete, ovvero /etc/udev/rules.d/70-persistent-net.rules:

root@linux-box:~$ cat /etc/udev/rules.d/70-persistent-net.rules

# PCI device 0x10ec:/sys/devices/pci0000:00/0000:00:1e.0/0000:04:01.0 (8139too)
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="00:40:f4:44:68:8e", ATTR{dev_id}=="0x0", ATTR{type}=="1", NAME="eth0"

La suddetta regola di esempio parla chiaro: appena viene identificata una scheda di rete recante come indirizzo MAC 00:40:f4:44:68:8e, come id 0x0 e come tipo 1, le dovrà essere assegnato come nome eth0.

Configurazione

A questo punto la scheda è pronta per essere configurata, secondo quanto definito all’interno del file /etc/network/interfaces:

auto eth0
iface eth0 inet static
address 192.168.11.1
netmask 255.255.255.0

Comandi di diagnostica

Se tutto è filato per il verso giusto, lanciando un semplice ifconfig, la suddetta scheda di rete sarà tra quelle disponibili:

root@linux-box:~$ ifconfig eth0
eth0      Link encap:Ethernet  IndirizzoHW 00:40:f4:44:68:8e
          indirizzo inet:192.168.1.1  Bcast:192.168.11.255  Maschera:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:1989751 errors:0 dropped:0 overruns:0 frame:0
          TX packets:3500430 errors:0 dropped:0 overruns:0 carrier:0
          collisioni:0 txqueuelen:1000
          Byte RX:339007498 (339.0 MB)  Byte TX:3756299292 (3.7 GB)
          Interrupt:16 Indirizzo base:0xe800

Inoltre, è possibile visualizzare il modulo in uso per interfacciarsi con la suddetta scheda, avvalendosi di ethtool con l’opzione -k:

root@linux-box:~$ ethtool -i eth0

il cui output sarà simile al seguente:

driver: 8139too
version: 0.9.28
firmware-version:
bus-info: 0000:04:01.0
supports-statistics: yes
supports-test: no
supports-eeprom-access: no
supports-register-dump: no

Per identificare, invece, alcuni dei suoi parametri di funzionamento, quali autonegoziazione, velocità, duplex, ecc., è sufficiente lanciare il comando:

root@linux-box:~# ethtool eth0

che ci darà un risultato simile al seguente:

Settings for eth0:
        Supported ports: [ TP MII ]
        Supported link modes:   10baseT/Half 10baseT/Full
                                100baseT/Half 100baseT/Full
        Supported pause frame use: No
        Supports auto-negotiation: Yes
        Advertised link modes:  10baseT/Half 10baseT/Full
                                100baseT/Half 100baseT/Full
        Advertised pause frame use: No
        Advertised auto-negotiation: Yes
        Link partner advertised link modes:  10baseT/Half 10baseT/Full
                                             100baseT/Half 100baseT/Full
        Link partner advertised pause frame use: Symmetric
        Link partner advertised auto-negotiation: Yes
        Speed: 100Mb/s
        Duplex: Full
        Port: MII
        PHYAD: 32
        Transceiver: internal
        Auto-negotiation: on
        Supports Wake-on: pumbg
        Wake-on: g
        Current message level: 0x00000007 (7)
                               drv probe link
        Link detected: yes

In alternativa, si possono ottenere delle informazioni più sintetiche utilizzando il più datato (e meno completo) mii-tool:

root@linux-box:~$ mii-tool eth0
eth0: negotiated 100baseTx-FD, link ok

Identificazione della causa

A valle delle riflessioni fatte fin’ora, tutto ciò che riguarda la scheda risulta coerente e conforme con quanto ci si aspettava, per cui è stato facile intuire, andando per esclusione, che si trattava, banalmente, di un guasto hardware.

Una volta sostituita la scheda, infatti, tutto ha ripreso a funzionare correttamente.

Alla prossima.

CentOS 6: gestione delle periferiche removibili mediante UDEV

Scenario

Macchina CentOS 6 dotata di 2 dischi rigidi, ovvero:

1) /dev/sda dove è presente la partizione di boot (sda1) e quella di root (sda2);

2) /dev/sdb che consta di un’unica partizione dedicata al salvataggio di dati.

CentOSProblema

Collegando una memoria flash di tipo USB (che funge da dongle per un applicativo in esecuzione sul server) essa viene inizialmente mappata come /dev/sdc. Dopo un riavvio della macchina, però, il kernel name assegnato alla stessa diventa /dev/sda e di conseguenza i 2 dischi rigidi vengono mappati come /dev/sdb e /dev/sdc. Ciò, fortunatamente, non va ad inficiare il funzionamento del server, in quanto il file /etc/fstab contiene gli UUID (univoci e soprattutto statici) assegnati a ciascuna partizione (anzichè il kernel name che, come abbiamo visto, può variare).

Nel mio caso, però, esiste un problema: poichè utilizzo uno specifico tool per monitorare lo stato delle partizioni, le quali vengono interrogate mediante il loro kernel name, dovrei modificare tale parametro di volta in volta, distinguendo il caso in cui i dischi rigidi vengono mappati in modo “standard”, ovvero come /dev/sda e /dev/sdb, dal caso in cui vengono riconosciuti come /dev/sdb e /dev/sdc (con la memoria flash che fa le veci di /dev/sda). Ergo, devo fare in modo che la memoria flash venga mappata sempre e comunque come /dev/sdd.

Soluzione

Quanto sopra può essere realizzato configurando opportunamente udev. Tale applicativo si basa su alcuni file di configurazione, dislocati su 2 directory differenti, ovvero:

1) /lib/udev/rules.d/ che contiene i file di configurazione di default (non vanno editati);

2) /etc/udev/rules.d che contiene i file di configurazione “customizzabili” dall’utente (il cui contenuto ha una maggiore priorità rispetto a quello dei file di default, “scavalcandoli” all’occorrenza).

Tutti i suddetti file vengono dati in pasto ad udev seguendo un ordine crescente dettato esclusivamente dal loro filename. Da ciò si capisce come mai il contenuto tipico della directory /etc/udev/rules.d è simile al seguente:

[root@linuxbox ~]# ls /etc/udev/rules.d/
60-pcmcia.rules  70-persistent-cd.rules    90-hal.rules
60-fprint-autosuspend.rules  60-raw.rules     70-persistent-net.rules  90-alsa.rules                98-kexec.rules

dove le direttive presenti nel file 60-pcmcia.rules vengono lette (ed eseguite) prima di quelle relative a 70-persistent-cd.rules.

Poichè vogliamo che il file contenente le nostre direttive custom venga valutato per primo, dobbiamo nominarlo come 10-local.rules. Creiamolo quindi utilizzando il comando:

[root@linuxbox ~]# touch /etc/udev/rules.d/10-local.rules

per poi popolarlo come vi indicherò più avanti.

Dopo aver creato il suddetto file, occorre individuare le caratteristiche tipiche di ciascun disco rigido e della memoria flash, utilizzando il comando:

[root@linuxbox ~]# udevadm info -a -n /dev/sdXY

dove X è la lettera che identifica il device ed Y (facoltativo) è il numero di partizione.

Ad esempio, per /dev/sda possiamo utilizzare il comando:

[root@linuxbox ~]# udevadm info -a -n /dev/sda

il cui output sarà simile al seguente:

custom logging function 0x7f90ec575030 registered
udevadm[29910]: custom logging function 0x7f90ec575030 registered
selinux=1
udevadm[29910]: selinux=1
calling: info
udevadm[29910]: calling: info
device 0x7f90ec599460 has devpath '/devices/pci0000:00/0000:00:0b.0/host0/target0:0:0/0:0:0:0/block/sda'
udevadm[29910]: device 0x7f90ec599460 has devpath '/devices/pci0000:00/0000:00:0b.0/host0/target0:0:0/0:0:0:0/block/sda'

Udevadm info starts with the device specified by the devpath and then
walks up the chain of parent devices. It prints for every device
found, all possible attributes in the udev rules key format.
A rule to match, can be composed by the attributes of the device
and the attributes from one single parent device.

  looking at device '/devices/pci0000:00/0000:00:0b.0/host0/target0:0:0/0:0:0:0/block/sda':
    KERNEL=="sda"
    SUBSYSTEM=="block"
    DRIVER==""
    ATTR{range}=="16"
    ATTR{ext_range}=="256"
    ATTR{removable}=="0"
    ATTR{ro}=="0"
    ATTR{size}=="976773168"
    ATTR{alignment_offset}=="0"
    ATTR{discard_alignment}=="0"
    ATTR{capability}=="52"
    ATTR{stat}==" 7302543  1320445 841562942 71451185  6119635 28157362 271395642 272451897        0 51376878 343882883"
    ATTR{inflight}=="       0        0"

device 0x7f90ec59ff90 has devpath '/devices/pci0000:00/0000:00:0b.0/host0/target0:0:0/0:0:0:0'
udevadm[29910]: device 0x7f90ec59ff90 has devpath '/devices/pci0000:00/0000:00:0b.0/host0/target0:0:0/0:0:0:0'
  looking at parent device '/devices/pci0000:00/0000:00:0b.0/host0/target0:0:0/0:0:0:0':
    KERNELS=="0:0:0:0"
    SUBSYSTEMS=="scsi"
    DRIVERS=="sd"
    ATTRS{device_blocked}=="0"
    ATTRS{type}=="0"
    ATTRS{scsi_level}=="6"
    ATTRS{vendor}=="ATA     "
    ATTRS{model}=="TOSHIBA HDWJ105 "
    ATTRS{rev}=="AX00"
    ATTRS{state}=="running"
    ATTRS{timeout}=="30"
    ATTRS{eh_timeout}=="10"
    ATTRS{iocounterbits}=="32"
    ATTRS{iorequest_cnt}=="0xd13eaf"
    ATTRS{iodone_cnt}=="0xcfd529"
    ATTRS{ioerr_cnt}=="0x1e"
    ATTRS{modalias}=="scsi:t-0x00"
    ATTRS{evt_media_change}=="0"
    ATTRS{evt_inquiry_change_reported}=="0"
    ATTRS{evt_capacity_change_reported}=="0"
    ATTRS{evt_soft_threshold_reached}=="0"
    ATTRS{evt_mode_parameter_change_reported}=="0"
    ATTRS{evt_lun_change_reported}=="0"
    ATTRS{dh_state}=="detached"
    ATTRS{queue_depth}=="31"
    ATTRS{queue_ramp_up_period}=="120000"
    ATTRS{queue_type}=="simple"
    ATTRS{unload_heads}=="0"

device 0x7f90ec5a1b20 has devpath '/devices/pci0000:00/0000:00:0b.0/host0/target0:0:0'
udevadm[29910]: device 0x7f90ec5a1b20 has devpath '/devices/pci0000:00/0000:00:0b.0/host0/target0:0:0'
  looking at parent device '/devices/pci0000:00/0000:00:0b.0/host0/target0:0:0':
    KERNELS=="target0:0:0"
    SUBSYSTEMS=="scsi"
    DRIVERS==""

device 0x7f90ec5b3e10 has devpath '/devices/pci0000:00/0000:00:0b.0/host0'
udevadm[29910]: device 0x7f90ec5b3e10 has devpath '/devices/pci0000:00/0000:00:0b.0/host0'
  looking at parent device '/devices/pci0000:00/0000:00:0b.0/host0':
    KERNELS=="host0"
    SUBSYSTEMS=="scsi"
    DRIVERS==""

device 0x7f90ec595fb0 has devpath '/devices/pci0000:00/0000:00:0b.0'
udevadm[29910]: device 0x7f90ec595fb0 has devpath '/devices/pci0000:00/0000:00:0b.0'
  looking at parent device '/devices/pci0000:00/0000:00:0b.0':
    KERNELS=="0000:00:0b.0"
    SUBSYSTEMS=="pci"
    DRIVERS=="ahci"
    ATTRS{vendor}=="0x10de"
    ATTRS{device}=="0x0ab4"
    ATTRS{subsystem_vendor}=="0x1043"
    ATTRS{subsystem_device}=="0x83e2"
    ATTRS{class}=="0x010185"
    ATTRS{irq}=="25"
    ATTRS{local_cpus}=="f"
    ATTRS{local_cpulist}=="0-3"
    ATTRS{modalias}=="pci:v000010DEd00000AB4sv00001043sd000083E2bc01sc01i85"
    ATTRS{numa_node}=="-1"
    ATTRS{enable}=="1"
    ATTRS{broken_parity_status}=="0"
    ATTRS{msi_bus}==""

device 0x7f90ec5a5200 has devpath '/devices/pci0000:00'
udevadm[29910]: device 0x7f90ec5a5200 has devpath '/devices/pci0000:00'
  looking at parent device '/devices/pci0000:00':
    KERNELS=="pci0000:00"
    SUBSYSTEMS==""
    DRIVERS==""

Discorso simile vale per la partizione 1 del disco /dev/sda:

[root@linuxbox ~]# udevadm info -a -n /dev/sda1

il cui output sarà simile a:

custom logging function 0x7ff9331c9030 registered
udevadm[30106]: custom logging function 0x7ff9331c9030 registered
selinux=1
udevadm[30106]: selinux=1
calling: info
udevadm[30106]: calling: info
device 0x7ff9331ed460 has devpath '/devices/pci0000:00/0000:00:0b.0/host0/target0:0:0/0:0:0:0/block/sda/sda1'
udevadm[30106]: device 0x7ff9331ed460 has devpath '/devices/pci0000:00/0000:00:0b.0/host0/target0:0:0/0:0:0:0/block/sda/sda1'

Udevadm info starts with the device specified by the devpath and then
walks up the chain of parent devices. It prints for every device
found, all possible attributes in the udev rules key format.
A rule to match, can be composed by the attributes of the device
and the attributes from one single parent device.

  looking at device '/devices/pci0000:00/0000:00:0b.0/host0/target0:0:0/0:0:0:0/block/sda/sda1':
    KERNEL=="sda1"
    SUBSYSTEM=="block"
    DRIVER==""
    ATTR{partition}=="1"
    ATTR{start}=="2048"
    ATTR{size}=="1024000"
    ATTR{alignment_offset}=="0"
    ATTR{discard_alignment}=="0"
    ATTR{stat}=="    2379      166   368458    10840       34       49      178     1663        0    11039    12484"
    ATTR{inflight}=="       0        0"

In particolare, utilizzeremo le informazioni riportate come attributi (ATTR) per identificare nel modo più preciso possibile il device/partizione su cui applicare le nostre regole.

Detto ciò, procediamo con la stesura del contenuto relativo al file 10-local.rules, che dovrà essere simile al seguente:

KERNEL=="sd?1", SUBSYSTEM=="block", ATTR{size}=="3915639", NAME="sdd1"
KERNEL=="sd*", SUBSYSTEM=="block", ATTR{size}=="3915776", ATTR{capability}=="53", NAME="sdd"
KERNEL=="sdb", SUBSYSTEM=="block", ATTR{size}=="976773168", ATTR{capability}=="52", NAME="sda"
KERNEL=="sdb1", SUBSYSTEM=="block", ATTR{size}=="1024000", NAME="sda1"
KERNEL=="sdb2", SUBSYSTEM=="block", ATTR{size}=="975747072", NAME="sda2"
KERNEL=="sdc", SUBSYSTEM=="block", ATTR{size}=="1953525168", ATTR{capability}=="52", NAME="sdb"
KERNEL=="sdc1", SUBSYSTEM=="block", ATTR{size}=="1953520002", NAME="sdb1"

La prima direttiva fa in modo che la partizione della memoria flash (con dimensione pari a 3915639 byte) venga rinominata in sdd1. La seconda direttiva, invece, agisce direttamente sul device, rinominandolo in sdd. Da notare che tutti i campi che contengono le condizioni da matchare per l’identificazione del dispositivo/partizione si avvalgono dell’operatore ==, mentre quelle che agiscono su di essi contengono l’operatore di assegnazione, ovvero =. Inoltre, nell’ambito delle suddette direttive, si fa uso dei caratteri speciali ? e *, con il primo che identifica un solo carattere alfanumerico, e * che identifica qualsiasi occorrenza di caratteri alfanumerici (anche nel caso in cui esse siano pari a 0).

Le rimanenti direttive, stilate sulla falsariga della prima, fanno sì che i dischi rigidi vengano rinominati rispettivamente in /dev/sda e /dev/sdb. Ovviamente tale operazione è da intendersi solo ed esclusivamente nel caso in cui, durante la fase di boot della macchina, la memoria flash sia già collegata ad una porta USB della stessa.

Non ci rimane che testare “in place” (e quindi senza reboot) le suddette regole lanciando il comando:

[root@linuxbox ~]# udevadm test /sys/block/sdX

per i device e:

[root@linuxbox ~]# udevadm test /sys/block/sdX/sdXY

per le parizioni, seguito da un:

[root@linuxbox ~]# ls -ilah /dev

per appurare che i dispositivi siano stati effettivamente mappati secondo le nostre necessità.

Per ora è tutto. A presto.