interfaces_test.go 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package netmon
  4. import (
  5. "encoding/json"
  6. "net"
  7. "net/netip"
  8. "testing"
  9. "tailscale.com/tstest"
  10. )
  11. func TestGetState(t *testing.T) {
  12. st, err := getState("")
  13. if err != nil {
  14. t.Fatal(err)
  15. }
  16. j, err := json.MarshalIndent(st, "", "\t")
  17. if err != nil {
  18. t.Errorf("JSON: %v", err)
  19. }
  20. t.Logf("Got: %s", j)
  21. t.Logf("As string: %s", st)
  22. }
  23. func TestLikelyHomeRouterIP(t *testing.T) {
  24. ipnet := func(s string) net.Addr {
  25. ip, ipnet, err := net.ParseCIDR(s)
  26. ipnet.IP = ip
  27. if err != nil {
  28. t.Fatal(err)
  29. }
  30. return ipnet
  31. }
  32. mockInterfaces := []Interface{
  33. // Interface that's not running
  34. {
  35. Interface: &net.Interface{
  36. Index: 1,
  37. MTU: 1500,
  38. Name: "down0",
  39. Flags: net.FlagBroadcast | net.FlagMulticast,
  40. },
  41. AltAddrs: []net.Addr{
  42. ipnet("10.0.0.100/8"),
  43. },
  44. },
  45. // Interface that's up, but only has an IPv6 address
  46. {
  47. Interface: &net.Interface{
  48. Index: 2,
  49. MTU: 1500,
  50. Name: "ipsixonly0",
  51. Flags: net.FlagUp | net.FlagBroadcast | net.FlagMulticast | net.FlagRunning,
  52. },
  53. AltAddrs: []net.Addr{
  54. ipnet("76f9:2e7d:55dd:48e1:48d0:763a:b591:b1bc/64"),
  55. },
  56. },
  57. // Fake interface with a gateway to the internet
  58. {
  59. Interface: &net.Interface{
  60. Index: 3,
  61. MTU: 1500,
  62. Name: "fake0",
  63. Flags: net.FlagUp | net.FlagBroadcast | net.FlagMulticast | net.FlagRunning,
  64. },
  65. AltAddrs: []net.Addr{
  66. ipnet("23a1:99c9:3a88:1d29:74d4:957b:2133:3f4e/64"),
  67. ipnet("192.168.7.100/24"),
  68. },
  69. },
  70. }
  71. // Mock out the responses from netInterfaces()
  72. tstest.Replace(t, &altNetInterfaces, func() ([]Interface, error) {
  73. return mockInterfaces, nil
  74. })
  75. // Mock out the likelyHomeRouterIP to return a known gateway.
  76. tstest.Replace(t, &likelyHomeRouterIP, func() (netip.Addr, netip.Addr, bool) {
  77. return netip.MustParseAddr("192.168.7.1"), netip.Addr{}, true
  78. })
  79. gw, my, ok := LikelyHomeRouterIP()
  80. if !ok {
  81. t.Fatal("expected success")
  82. }
  83. t.Logf("myIP = %v; gw = %v", my, gw)
  84. if want := netip.MustParseAddr("192.168.7.1"); gw != want {
  85. t.Errorf("got gateway %v; want %v", gw, want)
  86. }
  87. if want := netip.MustParseAddr("192.168.7.100"); my != want {
  88. t.Errorf("got self IP %v; want %v", my, want)
  89. }
  90. // Verify that no IP is returned if there are no IPv4 addresses on
  91. // local interfaces.
  92. t.Run("NoIPv4Addrs", func(t *testing.T) {
  93. tstest.Replace(t, &mockInterfaces, []Interface{
  94. // Interface that's up, but only has an IPv6 address
  95. {
  96. Interface: &net.Interface{
  97. Index: 2,
  98. MTU: 1500,
  99. Name: "en0",
  100. Flags: net.FlagUp | net.FlagBroadcast | net.FlagMulticast | net.FlagRunning,
  101. },
  102. AltAddrs: []net.Addr{
  103. ipnet("76f9:2e7d:55dd:48e1:48d0:763a:b591:b1bc/64"),
  104. },
  105. },
  106. })
  107. _, _, ok := LikelyHomeRouterIP()
  108. if ok {
  109. t.Fatal("expected no success")
  110. }
  111. })
  112. }
  113. // https://github.com/tailscale/tailscale/issues/10466
  114. func TestLikelyHomeRouterIP_Prefix(t *testing.T) {
  115. ipnet := func(s string) net.Addr {
  116. ip, ipnet, err := net.ParseCIDR(s)
  117. ipnet.IP = ip
  118. if err != nil {
  119. t.Fatal(err)
  120. }
  121. return ipnet
  122. }
  123. mockInterfaces := []Interface{
  124. // Valid and running interface that doesn't have a route to the
  125. // internet, and comes before the interface that does.
  126. {
  127. Interface: &net.Interface{
  128. Index: 1,
  129. MTU: 1500,
  130. Name: "docker0",
  131. Flags: net.FlagUp |
  132. net.FlagBroadcast |
  133. net.FlagMulticast |
  134. net.FlagRunning,
  135. },
  136. AltAddrs: []net.Addr{
  137. ipnet("172.17.0.0/16"),
  138. },
  139. },
  140. // Fake interface with a gateway to the internet.
  141. {
  142. Interface: &net.Interface{
  143. Index: 2,
  144. MTU: 1500,
  145. Name: "fake0",
  146. Flags: net.FlagUp |
  147. net.FlagBroadcast |
  148. net.FlagMulticast |
  149. net.FlagRunning,
  150. },
  151. AltAddrs: []net.Addr{
  152. ipnet("192.168.7.100/24"),
  153. },
  154. },
  155. }
  156. // Mock out the responses from netInterfaces()
  157. tstest.Replace(t, &altNetInterfaces, func() ([]Interface, error) {
  158. return mockInterfaces, nil
  159. })
  160. // Mock out the likelyHomeRouterIP to return a known gateway.
  161. tstest.Replace(t, &likelyHomeRouterIP, func() (netip.Addr, netip.Addr, bool) {
  162. return netip.MustParseAddr("192.168.7.1"), netip.Addr{}, true
  163. })
  164. gw, my, ok := LikelyHomeRouterIP()
  165. if !ok {
  166. t.Fatal("expected success")
  167. }
  168. t.Logf("myIP = %v; gw = %v", my, gw)
  169. if want := netip.MustParseAddr("192.168.7.1"); gw != want {
  170. t.Errorf("got gateway %v; want %v", gw, want)
  171. }
  172. if want := netip.MustParseAddr("192.168.7.100"); my != want {
  173. t.Errorf("got self IP %v; want %v", my, want)
  174. }
  175. }
  176. func TestLikelyHomeRouterIP_NoMocks(t *testing.T) {
  177. // Verify that this works properly when called on a real live system,
  178. // without any mocks.
  179. gw, my, ok := LikelyHomeRouterIP()
  180. t.Logf("LikelyHomeRouterIP: gw=%v my=%v ok=%v", gw, my, ok)
  181. }
  182. func TestIsUsableV6(t *testing.T) {
  183. tests := []struct {
  184. name string
  185. ip string
  186. want bool
  187. }{
  188. {"first ULA", "fc00::1", true},
  189. {"Tailscale", "fd7a:115c:a1e0::1", false},
  190. {"Cloud Run", "fddf:3978:feb1:d745::1", true},
  191. {"zeros", "0::0", false},
  192. {"Link Local", "fe80::1", false},
  193. {"Global", "2602::1", true},
  194. {"IPv4 public", "192.0.2.1", false},
  195. {"IPv4 private", "192.168.1.1", false},
  196. }
  197. for _, test := range tests {
  198. if got := isUsableV6(netip.MustParseAddr(test.ip)); got != test.want {
  199. t.Errorf("isUsableV6(%s) = %v, want %v", test.name, got, test.want)
  200. }
  201. }
  202. }
  203. func TestStateString(t *testing.T) {
  204. tests := []struct {
  205. name string
  206. s *State
  207. want string
  208. }{
  209. {
  210. name: "typical_linux",
  211. s: &State{
  212. DefaultRouteInterface: "eth0",
  213. Interface: map[string]Interface{
  214. "eth0": {
  215. Interface: &net.Interface{
  216. Flags: net.FlagUp,
  217. },
  218. },
  219. "wlan0": {
  220. Interface: &net.Interface{},
  221. },
  222. "lo": {
  223. Interface: &net.Interface{},
  224. },
  225. },
  226. InterfaceIPs: map[string][]netip.Prefix{
  227. "eth0": {
  228. netip.MustParsePrefix("10.0.0.2/8"),
  229. },
  230. "lo": {},
  231. },
  232. HaveV4: true,
  233. },
  234. want: `interfaces.State{defaultRoute=eth0 ifs={eth0:[10.0.0.2/8]} v4=true v6=false}`,
  235. },
  236. {
  237. name: "default_desc",
  238. s: &State{
  239. DefaultRouteInterface: "foo",
  240. Interface: map[string]Interface{
  241. "foo": {
  242. Desc: "a foo thing",
  243. Interface: &net.Interface{
  244. Flags: net.FlagUp,
  245. },
  246. },
  247. },
  248. },
  249. want: `interfaces.State{defaultRoute=foo (a foo thing) ifs={foo:[]} v4=false v6=false}`,
  250. },
  251. }
  252. for _, tt := range tests {
  253. t.Run(tt.name, func(t *testing.T) {
  254. got := tt.s.String()
  255. if got != tt.want {
  256. t.Errorf("wrong\n got: %s\nwant: %s\n", got, tt.want)
  257. }
  258. })
  259. }
  260. }
  261. // tests (*State).Equal
  262. func TestEqual(t *testing.T) {
  263. pfxs := func(addrs ...string) (ret []netip.Prefix) {
  264. for _, addr := range addrs {
  265. ret = append(ret, netip.MustParsePrefix(addr))
  266. }
  267. return ret
  268. }
  269. tests := []struct {
  270. name string
  271. s1, s2 *State
  272. want bool // implies !wantMajor
  273. }{
  274. {
  275. name: "eq_nil",
  276. want: true,
  277. },
  278. {
  279. name: "nil_mix",
  280. s2: new(State),
  281. want: false,
  282. },
  283. {
  284. name: "eq",
  285. s1: &State{
  286. DefaultRouteInterface: "foo",
  287. InterfaceIPs: map[string][]netip.Prefix{
  288. "foo": {netip.MustParsePrefix("10.0.1.2/16")},
  289. },
  290. },
  291. s2: &State{
  292. DefaultRouteInterface: "foo",
  293. InterfaceIPs: map[string][]netip.Prefix{
  294. "foo": {netip.MustParsePrefix("10.0.1.2/16")},
  295. },
  296. },
  297. want: true,
  298. },
  299. {
  300. name: "default-route-changed",
  301. s1: &State{
  302. DefaultRouteInterface: "foo",
  303. InterfaceIPs: map[string][]netip.Prefix{
  304. "foo": {netip.MustParsePrefix("10.0.1.2/16")},
  305. },
  306. },
  307. s2: &State{
  308. DefaultRouteInterface: "bar",
  309. InterfaceIPs: map[string][]netip.Prefix{
  310. "foo": {netip.MustParsePrefix("10.0.1.2/16")},
  311. },
  312. },
  313. want: false,
  314. },
  315. {
  316. name: "some-interface-ips-changed",
  317. s1: &State{
  318. DefaultRouteInterface: "foo",
  319. InterfaceIPs: map[string][]netip.Prefix{
  320. "foo": {netip.MustParsePrefix("10.0.1.2/16")},
  321. },
  322. },
  323. s2: &State{
  324. DefaultRouteInterface: "foo",
  325. InterfaceIPs: map[string][]netip.Prefix{
  326. "foo": {netip.MustParsePrefix("10.0.1.3/16")},
  327. },
  328. },
  329. want: false,
  330. },
  331. {
  332. name: "altaddrs-changed",
  333. s1: &State{
  334. Interface: map[string]Interface{
  335. "foo": {AltAddrs: []net.Addr{&net.TCPAddr{IP: net.ParseIP("1.2.3.4")}}},
  336. },
  337. },
  338. s2: &State{
  339. Interface: map[string]Interface{
  340. "foo": {AltAddrs: []net.Addr{&net.TCPAddr{IP: net.ParseIP("5.6.7.8")}}},
  341. },
  342. },
  343. want: false,
  344. },
  345. // See tailscale/corp#19124
  346. {
  347. name: "interface-removed",
  348. s1: &State{
  349. InterfaceIPs: map[string][]netip.Prefix{
  350. "rmnet16": pfxs("2607:1111:2222:3333:4444:5555:6666:7777/64"),
  351. "rmnet17": pfxs("2607:9999:8888:7777:666:5555:4444:3333/64"),
  352. "tun0": pfxs("100.64.1.2/32", "fd7a:115c:a1e0::1/128"),
  353. "v4-rmnet16": pfxs("192.0.0.4/32"),
  354. "wlan0": pfxs("10.0.0.111/24"), // removed below
  355. },
  356. },
  357. s2: &State{
  358. InterfaceIPs: map[string][]netip.Prefix{
  359. "rmnet16": pfxs("2607:1111:2222:3333:4444:5555:6666:7777/64"),
  360. "rmnet17": pfxs("2607:9999:8888:7777:666:5555:4444:3333/64"),
  361. "tun0": pfxs("100.64.1.2/32", "fd7a:115c:a1e0::1/128"),
  362. "v4-rmnet16": pfxs("192.0.0.4/32"),
  363. },
  364. },
  365. want: false,
  366. },
  367. }
  368. for _, tt := range tests {
  369. t.Run(tt.name, func(t *testing.T) {
  370. if got := tt.s2.Equal(tt.s1); got != tt.want {
  371. t.Errorf("Equal = %v; want %v", got, tt.want)
  372. }
  373. })
  374. }
  375. }