vishesh92 commented on PR #11800:
URL: https://github.com/apache/cloudstack/pull/11800#issuecomment-5566437000

   Thanks @mosys0815, I confirmed the change fixes #10996: on a multipath 
default route with `src`, the new command returns the interface holding the 
source IP (`hostip` in your setup), where the old command returned `32`.
   
   However, I found a regression while reviewing using AI. The new command only 
works when the default route carries a `src`/`prefsrc` attribute. When it 
doesn't, the inner `jq` prints `null`, the `select()` matches nothing, and the 
command returns an empty string with exit code 0. `getDefaultEthDevice()` then 
returns null, `getAllDefaultNicIps()` returns an empty list.
   
   **When does the default route have no `src`?**
   
   Whenever the route was installed by a static configuration rather than a 
DHCP client. I verified these on Ubuntu 24.04, Rocky 9 and openSUSE Leap 15.6 
docker container:
   
   | How the gateway was configured | Resulting default route | PR command 
returns |
   |---|---|---|
   | `ip route add default via GW dev eth0` | `default via GW dev eth0` | empty 
|
   | ifupdown static (`/etc/network/interfaces`, `gateway`) | `default via GW 
dev eth0 onlink` | empty |
   | systemd-networkd / netplan static (`Gateway=`) | `default via GW dev eth0 
proto static` | empty |
   | NetworkManager / nmcli `ipv4.method manual` + `ipv4.gateway` (EL9) | 
`default via GW dev eth0 proto static metric 550` | empty |
   | wicked static (`/etc/sysconfig/network/routes`, SUSE) | `default via GW 
dev eth0` | empty |
   | isc dhclient-script | `ip -4 route add default via ${router} dev 
${interface}`, no `src` | empty |
   | systemd-networkd or NetworkManager **DHCP** lease | `default via GW dev 
eth0 proto dhcp src IP metric ...` | eth0 |
   
   DHCP-managed hosts are fine, which is presumably why the Trillian run 
passed. Production management servers with a static IP are the ones that hit 
this.
   
   **`jq` dependency**
   
   As @weizhouapache noted, `jq` is not in `Requires` of any spec file nor in 
`debian/control`. Nothing in the management server's Java code shells out to 
`jq` today; the only user in the repo is the optional Proxmox extension script 
under `extensions/`. This would be the first core code path that needs it, and 
when it is absent the pipeline fails with `jq: command not found` and we end up 
in the same empty-result path (with a WARN in the log this time).
   
   **Reproduction**
   
   Save this as `repro.sh`. It builds dummy interfaces and the three route 
layouts in an isolated network stack, and prints what the old and new commands 
return for each:
   
   ```bash
   PR()  { ip -j a | jq -r '.[] | .addr_info | map(select(.local == "'`ip -j r 
s default | jq -r '.[0] | .prefsrc'`'")) | .[].label'; }
   OLD() { ip route show default 0.0.0.0/0 | head -1 | awk '{print $5}'; }
   show() { echo "  old=[$(OLD | tr '\n' ' ')]  PR=[$(PR 2>&1 | tr '\n' ' ')]"; 
}
   
   ip link add d0 type dummy; ip link set d0 up; ip addr add 10.0.0.2/24 dev d0
   ip link add d1 type dummy; ip link set d1 up; ip addr add 10.0.1.2/24 dev d1
   ip link add hostip type dummy; ip link set hostip up; ip addr add 
10.72.44.3/32 dev hostip
   ip route del default 2>/dev/null
   
   echo "1. default route WITHOUT src"
   ip route add default via 10.0.0.1 dev d0; show
   
   echo "2. default route WITH src"
   ip route replace default via 10.0.0.1 dev d0 src 10.0.0.2; show
   
   echo "3. multipath default route with src (#10996)"
   ip route del default
   ip route add default src 10.72.44.3 metric 32 nexthop via 10.0.0.1 dev d0 
weight 1 nexthop via 10.0.1.1 dev d1 weight 1
   ip route add default via 10.0.0.1 dev d0 src 10.0.0.2 metric 1024 onlink; 
show
   ```
   
   Run it in a throwaway container (or in a network namespace on a host: `sudo 
ip netns add t && sudo ip netns exec t bash repro.sh && sudo ip netns del t`):
   
   ```bash
   docker run --rm --cap-add NET_ADMIN -v "$PWD/repro.sh:/repro.sh:ro" 
ubuntu:24.04 \
     bash -c 'apt-get -qq update >/dev/null && apt-get -qq install -y iproute2 
jq >/dev/null; bash /repro.sh'
   ```
   
   Output:
   
   ```
   1. default route WITHOUT src
     old=[d0 ]  PR=[]
   2. default route WITH src
     old=[d0 ]  PR=[d0 ]
   3. multipath default route with src (#10996)
     old=[32 ]  PR=[hostip ]
   ```
   
   Case 1 is the regression, case 3 is the bug this PR fixes. To see it end to 
end, start a management server on a host whose default route has no `src` and 
check the SAN of the served certificate; it will contain only DNS entries:
   
   ```bash
   openssl s_client -connect <mgmt-ip>:9090 </dev/null 2>/dev/null | openssl 
x509 -noout -ext subjectAltName
   ```
   
   **Suggested fix**
   
   Keep the `src`-first lookup, since it picks the right interface for the L3 
case, but fall back to the route's `dev` when `src` is absent, and drop `jq` so 
no new dependency is needed. This returns `d0`, `d0`, `hostip` for the three 
cases above, `d0` for a multipath route without `src`, and empty when there is 
no default route, on both Ubuntu 24.04 and Rocky 9:
   
   ```bash
   r=$(ip -o route show default | head -1)
   s=$(grep -oP '\bsrc \K\S+' <<<"$r")
   if [ -n "$s" ]; then
     ip -o addr show to "$s" | awk '{print $2}' | head -1
   else
     grep -oP '\bdev \K\S+' <<<"$r" | head -1
   fi
   ```
   
   Alternatively parse the `ip -o route show default` line in Java and only 
shell out for `ip -o addr show to <src>`, which would make the logic 
unit-testable. If you'd rather keep `jq`, it needs to be added to the RPM specs 
and `debian/control` before this can merge.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to