KANS3 - Cilium CNI - 01/03

BPF/eBPF

BPF(Berkeley Packet Filter)

1992년 버클리 대학교에서 처음 개발된 고성능 네트워크 패킷 필터링 기술이다. BPF의 핵심 개념은 패킷이 커널로 들어올 때 사용자 정의 필터를 적용하여, 특정 조건에 맞는 패킷만을 선택하는 방식이며, 이는 불필요한 패킷을 필터링하여 시스템 리소스를 절약하고, 네트워크 애플리케이션이 필요한 데이터만 처리할 수 있도록 지원한다.

주요 특징

  • 효율성: BPF는 커널 내에서 직접 동작하여, 네트워크 패킷 필터링이 매우 빠릅니다. 이는 시스템 호출 수를 줄여주어 성능을 높이는 데 기여합니다.
  • 유연성: BPF는 다양한 조건을 통해 네트워크 패킷을 필터링할 수 있으며, TCP/UDP 포트, IP 주소, 패킷 내용 등 여러 기준으로 필터링 설정이 가능합니다.

eBPF(extended Berkeley Packet Filter)

BPF에서 확장된 것으로 다양한 시스템 기능을 위한 범용 도구로 확장되었습니다. eBPF는 리눅스 커널 내에서 사용자 정의 코드를 실행할 수 있게 해 주며, 시스템 호출, 트레이싱, 네트워크, 보안 모니터링 등에 사용됩니다.

  • 실시간 데이터 분석: eBPF는 커널 내에서 사용자 정의 프로그램을 실행하여 실시간으로 데이터를 수집하고 처리합니다. 이는 성능에 거의 영향을 미치지 않으면서 다양한 시스템 메트릭을 수집할 수 있게 해줍니다.
  • 네트워크 패킷 필터링 및 트래픽 관리: 네트워크 트래픽을 필터링하거나 관리하는 데 사용할 수 있습니다. eBPF는 XDP(eXpress Data Path)와 결합하여 네트워크 트래픽의 성능을 최적화하는 데 도움을 줄 수 있습니다.
  • 보안 모니터링 및 강화: eBPF는 실시간으로 특정 이벤트를 감시하거나 차단하는 데 사용될 수 있어 보안적인 측면에서도 유용합니다.
  • 고성능 트레이싱 및 디버깅: bpftrace, bcc(BPF Compiler Collection) 등의 도구를 통해 eBPF 프로그램을 작성하고 실행함으로써, 시스템에서 발생하는 다양한 이벤트를 모니터링하고 분석할 수 있습니다.

Cilium

Cilium은 eBPF를 기반으로 하는 K8s의 CNI 입니다.

  • eBPF 기반 고성능 네트워킹: Cilium은 eBPF를 통해 네트워크 트래픽을 효율적으로 처리하고 필터링합니다. 이 방식은 기존의 iptables나 커널 네트워킹 스택보다 성능이 우수하고, 더 세밀한 패킷 처리와 정책 적용이 가능합니다.
  • L3/L4 및 L7 보안 정책: Cilium은 전통적인 L3/L4 레벨(IP 및 포트)뿐만 아니라, L7 레벨(HTTP, gRPC 등)에서도 정책을 설정할 수 있습니다. 이를 통해 애플리케이션 레벨의 보안 제어가 가능하며, 마이크로서비스 간의 세부적인 보안 요구 사항을 충족할 수 있습니다.
  • 네트워크 트레이싱 및 가시성: Cilium은 트래픽 흐름과 정책 적용 상황을 시각화하고 분석하는 기능을 제공합니다. 이를 통해 네트워크 문제를 빠르게 진단하고 해결할 수 있으며, 정책 위반이나 예상치 못한 네트워크 트래픽을 실시간으로 모니터링할 수 있습니다.
  • 마이크로서비스 지원 및 확장성: 마이크로서비스 간의 복잡한 트래픽 경로와 정책 요구 사항을 충족하는 데 특화되어 있어, 대규모 클라우드 네이티브 애플리케이션 환경에서 효율적인 운영이 가능합니다.

구성요소

  • Cilium 에이전트(Cilium Agent): Cilium 에이전트는 각 Kubernetes 노드에 배포되어 실행되는 핵심 컴포넌트로, 네트워크 트래픽을 제어하고 보안 정책을 적용하는 역할을 합니다. eBPF를 사용해 데이터 플레인에 직접 작동하며, 네트워크 패킷을 필터링하고 정책을 기반으로 통신을 허용하거나 차단합니다. 이 에이전트는 네트워크 정책을 실시간으로 적용하고, CNI(Container Network Interface) 플러그인 역할도 수행합니다.

  • Cilium CLI는 사용자가 Cilium을 설치하고 관리할 수 있는 커맨드라인 인터페이스 도구입니다. Cilium 네트워크 구성 상태를 확인하거나 정책을 적용하는 데 사용됩니다.

  • Cilium Operator는 Kubernetes의 컨트롤 플레인에서 실행되며, IP 주소 할당 및 관리, Cilium 네트워크 정책을 Kubernetes 정책으로 변환하는 작업을 수행합니다. 이는 특히 Kubernetes와의 원활한 통합을 위해 중요한 역할을 합니다.

  • Cilium Hubble: Hubble은 Cilium의 네트워크 트래픽 가시성과 모니터링 기능을 강화하는 컴포넌트입니다. 네트워크 흐름을 실시간으로 시각화하고 분석할 수 있어, 마이크로서비스 간 트래픽 및 정책 적용 상태를 파악하는 데 유용합니다. 또한, Hubble UI를 통해 그래픽 기반의 대시보드를 제공하여 네트워크 이벤트를 직관적으로 모니터링할 수 있습니다.

실습

Cilium 배포

helm repo add cilium https://helm.cilium.io/
helm repo update

# 배포
helm install cilium cilium/cilium --version 1.16.3 --namespace kube-system \
--set k8sServiceHost=192.168.10.10 --set k8sServicePort=6443 --set debug.enabled=true \
--set rollOutCiliumPods=true --set routingMode=native --set autoDirectNodeRoutes=true \
--set bpf.masquerade=true --set bpf.hostRouting=true --set endpointRoutes.enabled=true \
--set ipam.mode=kubernetes --set k8s.requireIPv4PodCIDR=true --set kubeProxyReplacement=true \
--set ipv4NativeRoutingCIDR=192.168.0.0/16 --set installNoConntrackIptablesRules=true \
--set hubble.ui.enabled=true --set hubble.relay.enabled=true --set prometheus.enabled=true --set operator.prometheus.enabled=true --set hubble.metrics.enableOpenMetrics=true \
--set hubble.metrics.enabled="{dns:query;ignoreAAAA,drop,tcp,flow,port-distribution,icmp,httpV2:exemplars=true;labelsContext=source_ip\,source_namespace\,source_workload\,destination_ip\,destination_namespace\,destination_workload\,traffic_direction}" \
--set operator.replicas=1
# bypass-iptables-connection-tracking
helm upgrade cilium cilium/cilium --namespace kube-system --reuse-values --set installNoConntrackIptablesRules=true

# 확인
iptables -t raw -S | grep notrack
---
-A CILIUM_OUTPUT_raw -d 192.168.0.0/16 -m comment --comment "cilium: NOTRACK for pod traffic" -j CT --notrack
-A CILIUM_OUTPUT_raw -s 192.168.0.0/16 -m comment --comment "cilium: NOTRACK for pod traffic" -j CT --notrack
-A CILIUM_OUTPUT_raw -o lxc+ -m comment --comment "cilium: NOTRACK for proxy return traffic" -j CT --notrack
-A CILIUM_OUTPUT_raw -o cilium_host -m comment --comment "cilium: NOTRACK for proxy return traffic" -j CT --notrack
-A CILIUM_OUTPUT_raw -o lxc+ -m comment --comment "cilium: NOTRACK for L7 proxy upstream traffic" -j CT --notrack
-A CILIUM_OUTPUT_raw -o cilium_host -m comment --comment "cilium: NOTRACK for L7 proxy upstream traffic" -j CT --notrack
-A CILIUM_PRE_raw -d 192.168.0.0/16 -m comment --comment "cilium: NOTRACK for pod traffic" -j CT --notrack
-A CILIUM_PRE_raw -s 192.168.0.0/16 -m comment --comment "cilium: NOTRACK for pod traffic" -j CT --notrack
-A CILIUM_PRE_raw -m comment --comment "cilium: NOTRACK for proxy traffic" -j CT --notrack
# CLI 설치
CILIUM_CLI_VERSION=$(curl -s https://raw.githubusercontent.com/cilium/cilium-cli/main/stable.txt)
CLI_ARCH=amd64
if [ "$(uname -m)" = "aarch64" ]; then CLI_ARCH=arm64; fi
curl -L --fail --remote-name-all https://github.com/cilium/cilium-cli/releases/download/${CILIUM_CLI_VERSION}/cilium-linux-${CLI_ARCH}.tar.gz{,.sha256sum}
sha256sum --check cilium-linux-${CLI_ARCH}.tar.gz.sha256sum
sudo tar xzvfC cilium-linux-${CLI_ARCH}.tar.gz /usr/local/bin
rm cilium-linux-${CLI_ARCH}.tar.gz{,.sha256sum}

# 확인
cilium status --wait
---
    /¯¯\
 /¯¯\__/¯¯\    Cilium:             OK
 \__/¯¯\__/    Operator:           OK
 /¯¯\__/¯¯\    Envoy DaemonSet:    OK
 \__/¯¯\__/    Hubble Relay:       OK
    \__/       ClusterMesh:        disabled

DaemonSet              cilium             Desired: 3, Ready: 3/3, Available: 3/3
DaemonSet              cilium-envoy       Desired: 3, Ready: 3/3, Available: 3/3
Deployment             cilium-operator    Desired: 1, Ready: 1/1, Available: 1/1
Deployment             hubble-relay       Desired: 1, Ready: 1/1, Available: 1/1
Deployment             hubble-ui          Desired: 1, Ready: 1/1, Available: 1/1
Containers:            cilium             Running: 3
                       cilium-envoy       Running: 3
                       cilium-operator    Running: 1
                       hubble-relay       Running: 1
                       hubble-ui          Running: 1
Cluster Pods:          4/4 managed by Cilium
Helm chart version:    1.16.3
Image versions         cilium             quay.io/cilium/cilium:v1.16.3@sha256:62d2a09bbef840a46099ac4c69421c90f84f28d018d479749049011329aa7f28: 3
                       cilium-envoy       quay.io/cilium/cilium-envoy:v1.29.9-1728346947-0d05e48bfbb8c4737ec40d5781d970a550ed2bbd@sha256:42614a44e508f70d03a04470df5f61e3cffd22462471a0be0544cf116f2c50ba: 3
                       cilium-operator    quay.io/cilium/operator-generic:v1.16.3@sha256:6e2925ef47a1c76e183c48f95d4ce0d34a1e5e848252f910476c3e11ce1ec94b: 1
                       hubble-relay       quay.io/cilium/hubble-relay:v1.16.3@sha256:feb60efd767e0e7863a94689f4a8db56a0acc7c1d2b307dee66422e3dc25a089: 1
                       hubble-ui          quay.io/cilium/hubble-ui-backend:v0.13.1@sha256:0e0eed917653441fded4e7cdb096b7be6a3bddded5a2dd10812a27b1fc6ed95b: 1
                       hubble-ui          quay.io/cilium/hubble-ui:v0.13.1@sha256:e2e9313eb7caf64b0061d9da0efbdad59c6c461f6ca1752768942bfeda0796c6: 1

# cilium 데몬셋 파드 내에서 cilium 명령어로 상태 확인
export CILIUMPOD0=$(kubectl get -l k8s-app=cilium pods -n kube-system --field-selector spec.nodeName=k8s-s  -o jsonpath='{.items[0].metadata.name}')
alias c0="kubectl exec -it $CILIUMPOD0 -n kube-system -c cilium-agent -- cilium"
c0 status --verbose
---
KVStore:                Ok   Disabled
Kubernetes:             Ok   1.30 (v1.30.6) [linux/amd64]
Kubernetes APIs:        ["EndpointSliceOrEndpoint", "cilium/v2::CiliumClusterwideNetworkPolicy", "cilium/v2::CiliumEndpoint", "cilium/v2::CiliumNetworkPolicy", "cilium/v2::CiliumNode", "cilium/v2alpha1::CiliumCIDRGroup", "core/v1::Namespace", "core/v1::Pods", "core/v1::Service", "networking.k8s.io/v1::NetworkPolicy"]
KubeProxyReplacement:   True   [eth0   10.0.2.15 fe80::a00:27ff:fe87:8e29, eth1   192.168.10.10 fe80::a00:27ff:fe86:5bc0 (Direct Routing)]
Host firewall:          Disabled
SRv6:                   Disabled
CNI Chaining:           none
CNI Config file:        successfully wrote CNI configuration file to /host/etc/cni/net.d/05-cilium.conflist
Cilium:                 Ok   1.16.3 (v1.16.3-f2217191)
NodeMonitor:            Listening for events on 2 CPUs with 64x4096 of shared memory
Cilium health daemon:   Ok
IPAM:                   IPv4: 2/254 allocated from 172.16.0.0/24,
Allocated addresses:
  172.16.0.216 (router)
  172.16.0.243 (health)
IPv4 BIG TCP:           Disabled
IPv6 BIG TCP:           Disabled
BandwidthManager:       Disabled
Routing:                Network: Native   Host: BPF
Attach Mode:            Legacy TC
Device Mode:            veth
Masquerading:           BPF   [eth0, eth1]   192.168.0.0/16 [IPv4: Enabled, IPv6: Disabled]
Clock Source for BPF:   ktime
Controller Status:      20/20 healthy
  Name                                  Last s
...
# Native Routing 확인 : # 192.168.0.0/16 대역은 IP Masq 없이 라우팅
c0 status | grep KubeProxyReplacement
---
KubeProxyReplacement:    True   [eth0   10.0.2.15 fe80::a00:27ff:fe87:8e29, eth1   192.168.10.10 fe80::a00:27ff:fe86:5bc0 (Direct Routing)]

# enableIPv4Masquerade=true(기본값) , bpf.masquerade=true 확인
cilium config view | egrep 'enable-ipv4-masquerade|enable-bpf-masquerade'
---
enable-bpf-masquerade                             true
enable-ipv4-masquerade                            true
c0 status --verbose | grep Masquerading
---
Masquerading:           BPF   [eth0, eth1]   192.168.0.0/16 [IPv4: Enabled, IPv6: Disabled]

# eBPF-based ip-masq-agent
helm upgrade cilium cilium/cilium --namespace kube-system --reuse-values --set ipMasqAgent.enabled=true

#확인
cilium config view | grep -i masq
---
enable-bpf-masquerade                             true
enable-ip-masq-agent                              true
enable-ipv4-masquerade                            true
enable-ipv6-masquerade                            true
enable-masquerade-to-route-source                 false

c0 status --verbose | grep Masquerading
---
Masquerading:           BPF (ip-masq-agent)   [eth0, eth1]   192.168.0.0/16 [IPv4: Enabled, IPv6: Disabled]

k get cm -n kube-system cilium-config -o yaml  | grep ip-masq
  enable-ip-masq-agent: "true"
# 변수 지정
export CILIUMPOD0=$(kubectl get -l k8s-app=cilium pods -n kube-system --field-selector spec.nodeName=k8s-s  -o jsonpath='{.items[0].metadata.name}')
export CILIUMPOD1=$(kubectl get -l k8s-app=cilium pods -n kube-system --field-selector spec.nodeName=k8s-w1 -o jsonpath='{.items[0].metadata.name}')
export CILIUMPOD2=$(kubectl get -l k8s-app=cilium pods -n kube-system --field-selector spec.nodeName=k8s-w2 -o jsonpath='{.items[0].metadata.name}')

# 단축키(alias) 지정
alias c0="kubectl exec -it $CILIUMPOD0 -n kube-system -c cilium-agent -- cilium"
alias c1="kubectl exec -it $CILIUMPOD1 -n kube-system -c cilium-agent -- cilium"
alias c2="kubectl exec -it $CILIUMPOD2 -n kube-system -c cilium-agent -- cilium"

alias c0bpf="kubectl exec -it $CILIUMPOD0 -n kube-system -c cilium-agent -- bpftool"
alias c1bpf="kubectl exec -it $CILIUMPOD1 -n kube-system -c cilium-agent -- bpftool"
alias c2bpf="kubectl exec -it $CILIUMPOD2 -n kube-system -c cilium-agent -- bpftool"

# Hubble UI 접속
k patch -n kube-system svc hubble-ui -p '{"spec": {"type": "NodePort"}}'
HubbleUiNodePort=$(kubectl get svc -n kube-system hubble-ui -o jsonpath={.spec.ports[0].nodePort})
echo -e "Hubble UI URL = http://$(curl -s ipinfo.io/ip):$HubbleUiNodePort"
# hubble cli 설치
HUBBLE_VERSION=$(curl -s https://raw.githubusercontent.com/cilium/hubble/master/stable.txt)
HUBBLE_ARCH=amd64
if [ "$(uname -m)" = "aarch64" ]; then HUBBLE_ARCH=arm64; fi
curl -L --fail --remote-name-all https://github.com/cilium/hubble/releases/download/$HUBBLE_VERSION/hubble-linux-${HUBBLE_ARCH}.tar.gz{,.sha256sum}
sha256sum --check hubble-linux-${HUBBLE_ARCH}.tar.gz.sha256sum
sudo tar xzvfC hubble-linux-${HUBBLE_ARCH}.tar.gz /usr/local/bin
rm hubble-linux-${HUBBLE_ARCH}.tar.gz{,.sha256sum}

cilium hubble port-forward &

노드간 파드 통신 확인

# POD 배포
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
  name: netpod
  labels:
    app: netpod
spec:
  nodeName: k8s-s
  containers:
  - name: netshoot-pod
    image: nicolaka/netshoot
    command: ["tail"]
    args: ["-f", "/dev/null"]
  terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Pod
metadata:
  name: webpod1
  labels:
    app: webpod
spec:
  nodeName: k8s-w1
  containers:
  - name: container
    image: traefik/whoami
  terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Pod
metadata:
  name: webpod2
  labels:
    app: webpod
spec:
  nodeName: k8s-w2
  containers:
  - name: container
    image: traefik/whoami
  terminationGracePeriodSeconds: 0
EOF

# 배포 확인
c0 status --verbose | grep Allocated -A5
---
Allocated addresses:
  172.16.0.105 (default/netpod)
  172.16.0.216 (router)
  172.16.0.243 (health)
IPv4 BIG TCP:           Disabled
IPv6 BIG TCP:           Disabled
c1 status --verbose | grep Allocated -A5
---
Allocated addresses:
  172.16.1.107 (router)
  172.16.1.158 (default/webpod1)
  172.16.1.16 (health)
  172.16.1.227 (kube-system/hubble-ui-59bb4cb67b-bj8qq [restored])
  172.16.1.33 (kube-system/coredns-55cb58b774-b6vlm [restored])
c2 status --verbose | grep Allocated -A5
---
Allocated addresses:
  172.16.2.113 (router)
  172.16.2.207 (default/webpod2)
  172.16.2.58 (health)
  172.16.2.90 (kube-system/coredns-55cb58b774-hkxpm [restored])
IPv4 BIG TCP:           Disabled

c0 endpoint list
---
ENDPOINT   POLICY (ingress)   POLICY (egress)   IDENTITY   LABELS (source:key[=value])                                              IPv6   IPv4           STATUS
           ENFORCEMENT        ENFORCEMENT
1127       Disabled           Disabled          4          reserved:health                                                                 172.16.0.243   ready
3385       Disabled           Disabled          15360      k8s:app=netpod                                                                  172.16.0.105   ready
                                                           k8s:io.cilium.k8s.namespace.labels.kubernetes.io/metadata.name=default
                                                           k8s:io.cilium.k8s.policy.cluster=default
                                                           k8s:io.cilium.k8s.policy.serviceaccount=default
                                                           k8s:io.kubernetes.pod.namespace=default
4018       Disabled           Disabled          1          k8s:node-role.kubernetes.io/control-plane                                                      ready
                                                           k8s:node.kubernetes.io/exclude-from-external-load-balancers
                                                           reserved:host

c0 bpf endpoint list
---
IP ADDRESS        LOCAL ENDPOINT INFO
172.16.0.243:0    id=1127  sec_id=4     flags=0x0000 ifindex=9   mac=F2:C5:A6:B0:5B:79 nodemac=AE:FC:13:0B:6D:C0
172.16.0.216:0    (localhost)
192.168.10.10:0   (localhost)
10.0.2.15:0       (localhost)
172.16.0.105:0    id=3385  sec_id=15360 flags=0x0000 ifindex=11  mac=F2:58:A2:C8:85:09 nodemac=A6:76:C0:12:7C:06

c0 map get cilium_lxc
---
Key              Value                                                                                            State   Error
172.16.0.243:0   id=1127  sec_id=4     flags=0x0000 ifindex=9   mac=F2:C5:A6:B0:5B:79 nodemac=AE:FC:13:0B:6D:C0   sync
172.16.0.105:0   id=3385  sec_id=15360 flags=0x0000 ifindex=11  mac=F2:58:A2:C8:85:09 nodemac=A6:76:C0:12:7C:06   sync
# 테스트 POD 변수지정
NETPODIP=$(kubectl get pods netpod -o jsonpath='{.status.podIP}')
WEBPOD1IP=$(kubectl get pods webpod1 -o jsonpath='{.status.podIP}')
WEBPOD2IP=$(kubectl get pods webpod2 -o jsonpath='{.status.podIP}')

alias p0="kubectl exec -it netpod  -- "
alias p1="kubectl exec -it webpod1 -- "
alias p2="kubectl exec -it webpod2 -- "

# 확인
p0 ip -c -4 addr
---
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
10: eth0@if11: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000 link-netnsid 0
    inet 172.16.0.105/32 scope global eth0
       valid_lft forever preferred_lft forever
p0 route -n
---
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         172.16.0.216    0.0.0.0         UG    0      0        0 eth0
172.16.0.216    0.0.0.0         255.255.255.255 UH    0      0        0 eth0
p0 ping -c 1 $WEBPOD1IP && p0 ping -c 1 $WEBPOD2IP
---
PING 172.16.1.158 (172.16.1.158) 56(84) bytes of data.
64 bytes from 172.16.1.158: icmp_seq=1 ttl=62 time=0.741 ms

--- 172.16.1.158 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.741/0.741/0.741/0.000 ms
PING 172.16.2.207 (172.16.2.207) 56(84) bytes of data.
64 bytes from 172.16.2.207: icmp_seq=1 ttl=62 time=0.935 ms

--- 172.16.2.207 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.935/0.935/0.935/0.000 ms
p0 curl -s $WEBPOD1IP && p0 curl -s $WEBPOD2IP
---
Hostname: webpod1
IP: 127.0.0.1
IP: ::1
IP: 172.16.1.158
IP: fe80::38a0:30ff:fe33:1df
RemoteAddr: 172.16.0.105:55234
GET / HTTP/1.1
Host: 172.16.1.158
User-Agent: curl/8.7.1
Accept: */*

Hostname: webpod2
IP: 127.0.0.1
IP: ::1
IP: 172.16.2.207
IP: fe80::a414:5dff:fe93:5223
RemoteAddr: 172.16.0.105:57394
GET / HTTP/1.1
Host: 172.16.2.207
User-Agent: curl/8.7.1
Accept: */*

p0 ping -c 1 8.8.8.8 && p0 curl -s wttr.in/seoul
---
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=61 time=33.2 ms

--- 8.8.8.8 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 33.183/33.183/33.183/0.000 ms
command terminated with exit code 6
p0 ip -c neigh
---
172.16.0.216 dev eth0 lladdr a6:76:c0:12:7c:06 REACHABLE