router.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622
  1. //go:build e2e_testing
  2. // +build e2e_testing
  3. package router
  4. import (
  5. "context"
  6. "fmt"
  7. "net"
  8. "os"
  9. "path/filepath"
  10. "reflect"
  11. "strconv"
  12. "strings"
  13. "sync"
  14. "testing"
  15. "time"
  16. "github.com/google/gopacket"
  17. "github.com/google/gopacket/layers"
  18. "github.com/slackhq/nebula"
  19. "github.com/slackhq/nebula/header"
  20. "github.com/slackhq/nebula/iputil"
  21. "github.com/slackhq/nebula/udp"
  22. )
  23. type R struct {
  24. // Simple map of the ip:port registered on a control to the control
  25. // Basically a router, right?
  26. controls map[string]*nebula.Control
  27. // A map for inbound packets for a control that doesn't know about this address
  28. inNat map[string]*nebula.Control
  29. // A last used map, if an inbound packet hit the inNat map then
  30. // all return packets should use the same last used inbound address for the outbound sender
  31. // map[from address + ":" + to address] => ip:port to rewrite in the udp packet to receiver
  32. outNat map[string]net.UDPAddr
  33. // A map of vpn ip to the nebula control it belongs to
  34. vpnControls map[iputil.VpnIp]*nebula.Control
  35. flow []flowEntry
  36. // All interactions are locked to help serialize behavior
  37. sync.Mutex
  38. fn string
  39. cancelRender context.CancelFunc
  40. t *testing.T
  41. }
  42. type flowEntry struct {
  43. note string
  44. packet *packet
  45. }
  46. type packet struct {
  47. from *nebula.Control
  48. to *nebula.Control
  49. packet *udp.Packet
  50. tun bool // a packet pulled off a tun device
  51. rx bool // the packet was received by a udp device
  52. }
  53. type ExitType int
  54. const (
  55. // KeepRouting the function will get called again on the next packet
  56. KeepRouting ExitType = 0
  57. // ExitNow does not route this packet and exits immediately
  58. ExitNow ExitType = 1
  59. // RouteAndExit routes this packet and exits immediately afterwards
  60. RouteAndExit ExitType = 2
  61. )
  62. type ExitFunc func(packet *udp.Packet, receiver *nebula.Control) ExitType
  63. // NewR creates a new router to pass packets in a controlled fashion between the provided controllers.
  64. // The packet flow will be recorded in a file within the mermaid directory under the same name as the test.
  65. // Renders will occur automatically, roughly every 100ms, until a call to RenderFlow() is made
  66. func NewR(t *testing.T, controls ...*nebula.Control) *R {
  67. ctx, cancel := context.WithCancel(context.Background())
  68. if err := os.MkdirAll("mermaid", 0755); err != nil {
  69. panic(err)
  70. }
  71. r := &R{
  72. controls: make(map[string]*nebula.Control),
  73. vpnControls: make(map[iputil.VpnIp]*nebula.Control),
  74. inNat: make(map[string]*nebula.Control),
  75. outNat: make(map[string]net.UDPAddr),
  76. fn: filepath.Join("mermaid", fmt.Sprintf("%s.md", t.Name())),
  77. t: t,
  78. cancelRender: cancel,
  79. }
  80. // Try to remove our render file
  81. os.Remove(r.fn)
  82. for _, c := range controls {
  83. addr := c.GetUDPAddr()
  84. if _, ok := r.controls[addr]; ok {
  85. panic("Duplicate listen address: " + addr)
  86. }
  87. r.vpnControls[c.GetVpnIp()] = c
  88. r.controls[addr] = c
  89. }
  90. // Spin the renderer in case we go nuts and the test never completes
  91. go func() {
  92. clockSource := time.NewTicker(time.Millisecond * 100)
  93. defer clockSource.Stop()
  94. for {
  95. select {
  96. case <-ctx.Done():
  97. return
  98. case <-clockSource.C:
  99. r.renderFlow()
  100. }
  101. }
  102. }()
  103. return r
  104. }
  105. // AddRoute will place the nebula controller at the ip and port specified.
  106. // It does not look at the addr attached to the instance.
  107. // If a route is used, this will behave like a NAT for the return path.
  108. // Rewriting the source ip:port to what was last sent to from the origin
  109. func (r *R) AddRoute(ip net.IP, port uint16, c *nebula.Control) {
  110. r.Lock()
  111. defer r.Unlock()
  112. inAddr := net.JoinHostPort(ip.String(), fmt.Sprintf("%v", port))
  113. if _, ok := r.inNat[inAddr]; ok {
  114. panic("Duplicate listen address inNat: " + inAddr)
  115. }
  116. r.inNat[inAddr] = c
  117. }
  118. // RenderFlow renders the packet flow seen up until now and stops further automatic renders from happening.
  119. func (r *R) RenderFlow() {
  120. r.cancelRender()
  121. r.renderFlow()
  122. }
  123. func (r *R) renderFlow() {
  124. f, err := os.OpenFile(r.fn, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0644)
  125. if err != nil {
  126. panic(err)
  127. }
  128. var participants = map[string]struct{}{}
  129. var participansVals []string
  130. fmt.Fprintln(f, "```mermaid")
  131. fmt.Fprintln(f, "sequenceDiagram")
  132. // Assemble participants
  133. for _, e := range r.flow {
  134. if e.packet == nil {
  135. continue
  136. }
  137. addr := e.packet.from.GetUDPAddr()
  138. if _, ok := participants[addr]; ok {
  139. continue
  140. }
  141. participants[addr] = struct{}{}
  142. sanAddr := strings.Replace(addr, ":", "#58;", 1)
  143. participansVals = append(participansVals, sanAddr)
  144. fmt.Fprintf(
  145. f, " participant %s as Nebula: %s<br/>UDP: %s\n",
  146. sanAddr, e.packet.from.GetVpnIp(), sanAddr,
  147. )
  148. }
  149. // Print packets
  150. h := &header.H{}
  151. for _, e := range r.flow {
  152. if e.packet == nil {
  153. fmt.Fprintf(f, " note over %s: %s\n", strings.Join(participansVals, ", "), e.note)
  154. continue
  155. }
  156. p := e.packet
  157. if p.tun {
  158. fmt.Fprintln(f, r.formatUdpPacket(p))
  159. } else {
  160. if err := h.Parse(p.packet.Data); err != nil {
  161. panic(err)
  162. }
  163. line := "--x"
  164. if p.rx {
  165. line = "->>"
  166. }
  167. fmt.Fprintf(f,
  168. " %s%s%s: %s(%s), counter: %v\n",
  169. strings.Replace(p.from.GetUDPAddr(), ":", "#58;", 1),
  170. line,
  171. strings.Replace(p.to.GetUDPAddr(), ":", "#58;", 1),
  172. h.TypeName(), h.SubTypeName(), h.MessageCounter,
  173. )
  174. }
  175. }
  176. fmt.Fprintln(f, "```")
  177. }
  178. // InjectFlow can be used to record packet flow if the test is handling the routing on its own.
  179. // The packet is assumed to have been received
  180. func (r *R) InjectFlow(from, to *nebula.Control, p *udp.Packet) {
  181. r.Lock()
  182. defer r.Unlock()
  183. r.unlockedInjectFlow(from, to, p, false)
  184. }
  185. func (r *R) Log(arg ...any) {
  186. r.Lock()
  187. r.flow = append(r.flow, flowEntry{note: fmt.Sprint(arg...)})
  188. r.t.Log(arg...)
  189. r.Unlock()
  190. }
  191. func (r *R) Logf(format string, arg ...any) {
  192. r.Lock()
  193. r.flow = append(r.flow, flowEntry{note: fmt.Sprintf(format, arg...)})
  194. r.t.Logf(format, arg...)
  195. r.Unlock()
  196. }
  197. // unlockedInjectFlow is used by the router to record a packet has been transmitted, the packet is returned and
  198. // should be marked as received AFTER it has been placed on the receivers channel
  199. func (r *R) unlockedInjectFlow(from, to *nebula.Control, p *udp.Packet, tun bool) *packet {
  200. fp := &packet{
  201. from: from,
  202. to: to,
  203. packet: p.Copy(),
  204. tun: tun,
  205. }
  206. r.flow = append(r.flow, flowEntry{packet: fp})
  207. return fp
  208. }
  209. // OnceFrom will route a single packet from sender then return
  210. // If the router doesn't have the nebula controller for that address, we panic
  211. func (r *R) OnceFrom(sender *nebula.Control) {
  212. r.RouteExitFunc(sender, func(*udp.Packet, *nebula.Control) ExitType {
  213. return RouteAndExit
  214. })
  215. }
  216. // RouteUntilTxTun will route for sender and return when a packet is seen on receivers tun
  217. // If the router doesn't have the nebula controller for that address, we panic
  218. func (r *R) RouteUntilTxTun(sender *nebula.Control, receiver *nebula.Control) []byte {
  219. tunTx := receiver.GetTunTxChan()
  220. udpTx := sender.GetUDPTxChan()
  221. for {
  222. select {
  223. // Maybe we already have something on the tun for us
  224. case b := <-tunTx:
  225. r.Lock()
  226. np := udp.Packet{Data: make([]byte, len(b))}
  227. copy(np.Data, b)
  228. r.unlockedInjectFlow(receiver, receiver, &np, true)
  229. r.Unlock()
  230. return b
  231. // Nope, lets push the sender along
  232. case p := <-udpTx:
  233. outAddr := sender.GetUDPAddr()
  234. r.Lock()
  235. inAddr := net.JoinHostPort(p.ToIp.String(), fmt.Sprintf("%v", p.ToPort))
  236. c := r.getControl(outAddr, inAddr, p)
  237. if c == nil {
  238. r.Unlock()
  239. panic("No control for udp tx")
  240. }
  241. fp := r.unlockedInjectFlow(sender, c, p, false)
  242. c.InjectUDPPacket(p)
  243. fp.rx = true
  244. r.Unlock()
  245. }
  246. }
  247. }
  248. // RouteForAllUntilTxTun will route for everyone and return when a packet is seen on receivers tun
  249. // If the router doesn't have the nebula controller for that address, we panic
  250. func (r *R) RouteForAllUntilTxTun(receiver *nebula.Control) []byte {
  251. sc := make([]reflect.SelectCase, len(r.controls)+1)
  252. cm := make([]*nebula.Control, len(r.controls)+1)
  253. i := 0
  254. sc[i] = reflect.SelectCase{
  255. Dir: reflect.SelectRecv,
  256. Chan: reflect.ValueOf(receiver.GetTunTxChan()),
  257. Send: reflect.Value{},
  258. }
  259. cm[i] = receiver
  260. i++
  261. for _, c := range r.controls {
  262. sc[i] = reflect.SelectCase{
  263. Dir: reflect.SelectRecv,
  264. Chan: reflect.ValueOf(c.GetUDPTxChan()),
  265. Send: reflect.Value{},
  266. }
  267. cm[i] = c
  268. i++
  269. }
  270. for {
  271. x, rx, _ := reflect.Select(sc)
  272. r.Lock()
  273. if x == 0 {
  274. // we are the tun tx, we can exit
  275. p := rx.Interface().([]byte)
  276. np := udp.Packet{Data: make([]byte, len(p))}
  277. copy(np.Data, p)
  278. r.unlockedInjectFlow(cm[x], cm[x], &np, true)
  279. r.Unlock()
  280. return p
  281. } else {
  282. // we are a udp tx, route and continue
  283. p := rx.Interface().(*udp.Packet)
  284. outAddr := cm[x].GetUDPAddr()
  285. inAddr := net.JoinHostPort(p.ToIp.String(), fmt.Sprintf("%v", p.ToPort))
  286. c := r.getControl(outAddr, inAddr, p)
  287. if c == nil {
  288. r.Unlock()
  289. panic("No control for udp tx")
  290. }
  291. fp := r.unlockedInjectFlow(cm[x], c, p, false)
  292. c.InjectUDPPacket(p)
  293. fp.rx = true
  294. }
  295. r.Unlock()
  296. }
  297. }
  298. // RouteExitFunc will call the whatDo func with each udp packet from sender.
  299. // whatDo can return:
  300. // - exitNow: the packet will not be routed and this call will return immediately
  301. // - routeAndExit: this call will return immediately after routing the last packet from sender
  302. // - keepRouting: the packet will be routed and whatDo will be called again on the next packet from sender
  303. func (r *R) RouteExitFunc(sender *nebula.Control, whatDo ExitFunc) {
  304. h := &header.H{}
  305. for {
  306. p := sender.GetFromUDP(true)
  307. r.Lock()
  308. if err := h.Parse(p.Data); err != nil {
  309. panic(err)
  310. }
  311. outAddr := sender.GetUDPAddr()
  312. inAddr := net.JoinHostPort(p.ToIp.String(), fmt.Sprintf("%v", p.ToPort))
  313. receiver := r.getControl(outAddr, inAddr, p)
  314. if receiver == nil {
  315. r.Unlock()
  316. panic("Can't route for host: " + inAddr)
  317. }
  318. e := whatDo(p, receiver)
  319. switch e {
  320. case ExitNow:
  321. r.Unlock()
  322. return
  323. case RouteAndExit:
  324. fp := r.unlockedInjectFlow(sender, receiver, p, false)
  325. receiver.InjectUDPPacket(p)
  326. fp.rx = true
  327. r.Unlock()
  328. return
  329. case KeepRouting:
  330. fp := r.unlockedInjectFlow(sender, receiver, p, false)
  331. receiver.InjectUDPPacket(p)
  332. fp.rx = true
  333. default:
  334. panic(fmt.Sprintf("Unknown exitFunc return: %v", e))
  335. }
  336. r.Unlock()
  337. }
  338. }
  339. // RouteUntilAfterMsgType will route for sender until a message type is seen and sent from sender
  340. // If the router doesn't have the nebula controller for that address, we panic
  341. func (r *R) RouteUntilAfterMsgType(sender *nebula.Control, msgType header.MessageType, subType header.MessageSubType) {
  342. h := &header.H{}
  343. r.RouteExitFunc(sender, func(p *udp.Packet, r *nebula.Control) ExitType {
  344. if err := h.Parse(p.Data); err != nil {
  345. panic(err)
  346. }
  347. if h.Type == msgType && h.Subtype == subType {
  348. return RouteAndExit
  349. }
  350. return KeepRouting
  351. })
  352. }
  353. func (r *R) RouteForAllUntilAfterMsgTypeTo(receiver *nebula.Control, msgType header.MessageType, subType header.MessageSubType) {
  354. h := &header.H{}
  355. r.RouteForAllExitFunc(func(p *udp.Packet, r *nebula.Control) ExitType {
  356. if r != receiver {
  357. return KeepRouting
  358. }
  359. if err := h.Parse(p.Data); err != nil {
  360. panic(err)
  361. }
  362. if h.Type == msgType && h.Subtype == subType {
  363. return RouteAndExit
  364. }
  365. return KeepRouting
  366. })
  367. }
  368. func (r *R) InjectUDPPacket(sender, receiver *nebula.Control, packet *udp.Packet) {
  369. r.Lock()
  370. defer r.Unlock()
  371. fp := r.unlockedInjectFlow(sender, receiver, packet, false)
  372. receiver.InjectUDPPacket(packet)
  373. fp.rx = true
  374. }
  375. // RouteForUntilAfterToAddr will route for sender and return only after it sees and sends a packet destined for toAddr
  376. // finish can be any of the exitType values except `keepRouting`, the default value is `routeAndExit`
  377. // If the router doesn't have the nebula controller for that address, we panic
  378. func (r *R) RouteForUntilAfterToAddr(sender *nebula.Control, toAddr *net.UDPAddr, finish ExitType) {
  379. if finish == KeepRouting {
  380. finish = RouteAndExit
  381. }
  382. r.RouteExitFunc(sender, func(p *udp.Packet, r *nebula.Control) ExitType {
  383. if p.ToIp.Equal(toAddr.IP) && p.ToPort == uint16(toAddr.Port) {
  384. return finish
  385. }
  386. return KeepRouting
  387. })
  388. }
  389. // RouteForAllExitFunc will route for every registered controller and calls the whatDo func with each udp packet from
  390. // whatDo can return:
  391. // - exitNow: the packet will not be routed and this call will return immediately
  392. // - routeAndExit: this call will return immediately after routing the last packet from sender
  393. // - keepRouting: the packet will be routed and whatDo will be called again on the next packet from sender
  394. func (r *R) RouteForAllExitFunc(whatDo ExitFunc) {
  395. sc := make([]reflect.SelectCase, len(r.controls))
  396. cm := make([]*nebula.Control, len(r.controls))
  397. i := 0
  398. for _, c := range r.controls {
  399. sc[i] = reflect.SelectCase{
  400. Dir: reflect.SelectRecv,
  401. Chan: reflect.ValueOf(c.GetUDPTxChan()),
  402. Send: reflect.Value{},
  403. }
  404. cm[i] = c
  405. i++
  406. }
  407. for {
  408. x, rx, _ := reflect.Select(sc)
  409. r.Lock()
  410. p := rx.Interface().(*udp.Packet)
  411. outAddr := cm[x].GetUDPAddr()
  412. inAddr := net.JoinHostPort(p.ToIp.String(), fmt.Sprintf("%v", p.ToPort))
  413. receiver := r.getControl(outAddr, inAddr, p)
  414. if receiver == nil {
  415. r.Unlock()
  416. panic("Can't route for host: " + inAddr)
  417. }
  418. e := whatDo(p, receiver)
  419. switch e {
  420. case ExitNow:
  421. r.Unlock()
  422. return
  423. case RouteAndExit:
  424. fp := r.unlockedInjectFlow(cm[x], receiver, p, false)
  425. receiver.InjectUDPPacket(p)
  426. fp.rx = true
  427. r.Unlock()
  428. return
  429. case KeepRouting:
  430. fp := r.unlockedInjectFlow(cm[x], receiver, p, false)
  431. receiver.InjectUDPPacket(p)
  432. fp.rx = true
  433. default:
  434. panic(fmt.Sprintf("Unknown exitFunc return: %v", e))
  435. }
  436. r.Unlock()
  437. }
  438. }
  439. // FlushAll will route for every registered controller, exiting once there are no packets left to route
  440. func (r *R) FlushAll() {
  441. sc := make([]reflect.SelectCase, len(r.controls))
  442. cm := make([]*nebula.Control, len(r.controls))
  443. i := 0
  444. for _, c := range r.controls {
  445. sc[i] = reflect.SelectCase{
  446. Dir: reflect.SelectRecv,
  447. Chan: reflect.ValueOf(c.GetUDPTxChan()),
  448. Send: reflect.Value{},
  449. }
  450. cm[i] = c
  451. i++
  452. }
  453. // Add a default case to exit when nothing is left to send
  454. sc = append(sc, reflect.SelectCase{
  455. Dir: reflect.SelectDefault,
  456. Chan: reflect.Value{},
  457. Send: reflect.Value{},
  458. })
  459. for {
  460. x, rx, ok := reflect.Select(sc)
  461. if !ok {
  462. return
  463. }
  464. r.Lock()
  465. p := rx.Interface().(*udp.Packet)
  466. outAddr := cm[x].GetUDPAddr()
  467. inAddr := net.JoinHostPort(p.ToIp.String(), fmt.Sprintf("%v", p.ToPort))
  468. receiver := r.getControl(outAddr, inAddr, p)
  469. if receiver == nil {
  470. r.Unlock()
  471. panic("Can't route for host: " + inAddr)
  472. }
  473. r.Unlock()
  474. }
  475. }
  476. // getControl performs or seeds NAT translation and returns the control for toAddr, p from fields may change
  477. // This is an internal router function, the caller must hold the lock
  478. func (r *R) getControl(fromAddr, toAddr string, p *udp.Packet) *nebula.Control {
  479. if newAddr, ok := r.outNat[fromAddr+":"+toAddr]; ok {
  480. p.FromIp = newAddr.IP
  481. p.FromPort = uint16(newAddr.Port)
  482. }
  483. c, ok := r.inNat[toAddr]
  484. if ok {
  485. sHost, sPort, err := net.SplitHostPort(toAddr)
  486. if err != nil {
  487. panic(err)
  488. }
  489. port, err := strconv.Atoi(sPort)
  490. if err != nil {
  491. panic(err)
  492. }
  493. r.outNat[c.GetUDPAddr()+":"+fromAddr] = net.UDPAddr{
  494. IP: net.ParseIP(sHost),
  495. Port: port,
  496. }
  497. return c
  498. }
  499. return r.controls[toAddr]
  500. }
  501. func (r *R) formatUdpPacket(p *packet) string {
  502. packet := gopacket.NewPacket(p.packet.Data, layers.LayerTypeIPv4, gopacket.Lazy)
  503. v4 := packet.Layer(layers.LayerTypeIPv4).(*layers.IPv4)
  504. if v4 == nil {
  505. panic("not an ipv4 packet")
  506. }
  507. from := "unknown"
  508. if c, ok := r.vpnControls[iputil.Ip2VpnIp(v4.SrcIP)]; ok {
  509. from = c.GetUDPAddr()
  510. }
  511. udp := packet.Layer(layers.LayerTypeUDP).(*layers.UDP)
  512. if udp == nil {
  513. panic("not a udp packet")
  514. }
  515. data := packet.ApplicationLayer()
  516. return fmt.Sprintf(
  517. " %s-->>%s: src port: %v<br/>dest port: %v<br/>data: \"%v\"\n",
  518. strings.Replace(from, ":", "#58;", 1),
  519. strings.Replace(p.to.GetUDPAddr(), ":", "#58;", 1),
  520. udp.SrcPort,
  521. udp.DstPort,
  522. string(data.Payload()),
  523. )
  524. }