config.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696
  1. package daemon
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "io"
  6. "log"
  7. "net"
  8. "net/netip"
  9. "os"
  10. "strconv"
  11. "strings"
  12. "time"
  13. "github.com/joho/godotenv"
  14. )
  15. const LogMsgTmpl = "YOSAI Daemon ||| time: %s ||| %s\n"
  16. var EnvironmentVariables = []string{
  17. "HASHICORP_VAULT_KEY",
  18. }
  19. const DefaultConfigLoc = "./.config.json"
  20. type DaemonConfigIO interface {
  21. Get() Configuration
  22. Log(data ...string)
  23. ConfigRouter(msg SockMessage) SockMessage
  24. Save(Configuration) error
  25. }
  26. /*
  27. TODO:
  28. - Make an implementation for using a configuration server, like a database or maybe a custom API
  29. - Have only 1 SSH key secret type in the keyring, and propogate it into the other systems more intelligently
  30. - intelligent keyring bootstrapping
  31. [KEYS]
  32. - read a SSH key for the VPS_SSH_KEY key
  33. - read in another SSH key for the GIT_SSH_KEY incase theyre different
  34. - Cloud API key
  35. - Secrets API key
  36. - Ansible API key
  37. - VPS Root
  38. - credentials
  39. - ssh key
  40. - VPS service account
  41. - credentials
  42. - ssh key
  43. [SERVICES]
  44. In order of priority:
  45. - Environment variables
  46. - configuration file
  47. - configuration server
  48. - user supplied input
  49. - Create a configuration server with REST API, grabs config based on
  50. - LDAP
  51. - Local
  52. Configuration server should have a web UI that allows to create a config via a form, and then
  53. save it to that users account.
  54. I would also like to get Hashicorp vault working with LDAP, so that I can build a large portion of this
  55. around LDAP. Instead of needing to supply the root token to HCV, I can have the client use their LDAP credentials
  56. all around, in HCV, the config server, and semaphore.
  57. What else needs to be done to allow for seemless experience for a 'user' across clients?
  58. - No on-system stored data
  59. - Easy bootstrapping
  60. */
  61. /*
  62. Loads in the environment variable file at path, and then validates that all values in vars is present
  63. :param path: the path to the .env file
  64. :param vars: the list of variables to check were loaded by godotenv.Load()
  65. */
  66. func LoadAndVerifyEnv(path string, vars []string) error {
  67. err := godotenv.Load(".env")
  68. if err != nil {
  69. return err
  70. }
  71. var missing []string
  72. for i := range vars {
  73. val := os.Getenv(vars[i])
  74. if val == "" {
  75. missing = append(missing, vars[i])
  76. }
  77. }
  78. if len(missing) != 0 {
  79. return &EnvironmentVariableNotSet{Vars: missing}
  80. }
  81. return nil
  82. }
  83. type EnvironmentVariableNotSet struct {
  84. Vars []string
  85. }
  86. func (e *EnvironmentVariableNotSet) Error() string {
  87. return fmt.Sprintf("Environment variables: %v not set!", e.Vars)
  88. }
  89. func BlankEnv(path string) error {
  90. var data string
  91. for i := range EnvironmentVariables {
  92. data = data + fmt.Sprintf("%s=\n", EnvironmentVariables[i])
  93. }
  94. return os.WriteFile(path, []byte(data), 0666)
  95. }
  96. /*
  97. Wrapping the add peer functionality in a router friendly interface
  98. :param msg: a message to be parsed from the daemon socket
  99. */
  100. func (c *Configuration) AddPeerHandler(msg SockMessage) SockMessage {
  101. var peer VpnClient
  102. err := json.Unmarshal(msg.Body, &peer)
  103. if err != nil {
  104. return *NewSockMessage(MsgResponse, REQUEST_FAILED, []byte(err.Error()))
  105. }
  106. addr, err := c.GetAvailableVpnIpv4()
  107. if err != nil {
  108. return *NewSockMessage(MsgResponse, REQUEST_FAILED, []byte(err.Error()))
  109. }
  110. return *NewSockMessage(MsgResponse, REQUEST_OK, []byte("Client: "+c.AddClient(addr, peer.Pubkey, peer.Name)+" Successfully added."))
  111. }
  112. /*
  113. Wrapping the delete peer functionality in a router friendly interface
  114. :param msg: a message to be parsed from the daemon socket
  115. */
  116. func (c *Configuration) DeletePeerHandler(msg SockMessage) SockMessage {
  117. var req VpnClient
  118. err := json.Unmarshal(msg.Body, &req)
  119. if err != nil {
  120. return *NewSockMessage(MsgResponse, REQUEST_FAILED, []byte(err.Error()))
  121. }
  122. peer, err := c.GetClient(req.Name)
  123. if err != nil {
  124. return *NewSockMessage(MsgResponse, REQUEST_FAILED, []byte(err.Error()))
  125. }
  126. delete(c.Service.Clients, peer.Name)
  127. err = c.FreeAddress(peer.VpnIpv4.String())
  128. if err != nil {
  129. return *NewSockMessage(MsgResponse, REQUEST_FAILED, []byte(err.Error()))
  130. }
  131. return *NewSockMessage(MsgResponse, REQUEST_OK, []byte("Client: "+peer.Name+" Successfully deleted from the config."))
  132. }
  133. /*
  134. Wrapping the add server functionality in a router friendly interface
  135. :param msg: a message to be parsed from the daemon socket
  136. */
  137. func (c *Configuration) AddServerHandler(msg SockMessage) SockMessage {
  138. var req VpnServer
  139. err := json.Unmarshal(msg.Body, &req)
  140. if err != nil {
  141. return *NewSockMessage(MsgResponse, REQUEST_FAILED, []byte(err.Error()))
  142. }
  143. addr, err := c.GetAvailableVpnIpv4()
  144. if err != nil {
  145. return *NewSockMessage(MsgResponse, REQUEST_FAILED, []byte(err.Error()))
  146. }
  147. name := c.AddServer(addr, req.Name, req.WanIpv4, req.Port)
  148. c.Log("address: ", addr.String(), "name:", name)
  149. return *NewSockMessage(MsgResponse, REQUEST_OK, []byte("Server: "+name+" Successfully added."))
  150. }
  151. /*
  152. Wrapping the delete server functionality in a router friendly interface
  153. :param msg: a message to be parsed from the daemon socket
  154. */
  155. func (c *Configuration) DeleteServerHandler(msg SockMessage) SockMessage {
  156. var req VpnServer
  157. err := json.Unmarshal(msg.Body, &req)
  158. if err != nil {
  159. return *NewSockMessage(MsgResponse, REQUEST_FAILED, []byte(err.Error()))
  160. }
  161. server, err := c.GetServer(req.Name)
  162. if err != nil {
  163. return *NewSockMessage(MsgResponse, REQUEST_FAILED, []byte(err.Error()))
  164. }
  165. delete(c.Service.Servers, server.Name)
  166. err = c.FreeAddress(server.VpnIpv4.String())
  167. if err != nil {
  168. return *NewSockMessage(MsgResponse, REQUEST_FAILED, []byte(err.Error()))
  169. }
  170. return *NewSockMessage(MsgResponse, REQUEST_OK, []byte("Server: "+server.Name+" Successfully deleted from the config."))
  171. }
  172. /*
  173. Wrapping the show config functionality in a router friendly interface
  174. :param msg: a message to be parsed from the daemon socket
  175. */
  176. func (c *Configuration) ShowConfigHandler(msg SockMessage) SockMessage {
  177. b, err := json.MarshalIndent(&c, "", " ")
  178. if err != nil {
  179. return *NewSockMessage(MsgResponse, REQUEST_FAILED, []byte(err.Error()))
  180. }
  181. return *NewSockMessage(MsgResponse, REQUEST_OK, b)
  182. }
  183. /*
  184. Wrapping the save config functionality in a router friendly interface
  185. :param msg: a message to be parsed from the daemon socket
  186. */
  187. func (c *Configuration) SaveConfigHandler(msg SockMessage) SockMessage {
  188. err := c.Save(DefaultConfigLoc)
  189. if err != nil {
  190. return *NewSockMessage(MsgResponse, REQUEST_FAILED, []byte(err.Error()))
  191. }
  192. return *NewSockMessage(MsgResponse, REQUEST_OK, []byte("Configuration saved successfully."))
  193. }
  194. /*
  195. Wrapping the reload config functionality in a router friendly interface
  196. :param msg: a message to be parsed from the daemon socket
  197. */
  198. func (c *Configuration) ReloadConfigHandler(msg SockMessage) SockMessage {
  199. b, err := os.ReadFile(DefaultConfigLoc)
  200. if err != nil {
  201. return *NewSockMessage(MsgResponse, REQUEST_FAILED, []byte(err.Error()))
  202. }
  203. err = json.Unmarshal(b, c)
  204. if err != nil {
  205. return *NewSockMessage(MsgResponse, REQUEST_FAILED, []byte(err.Error()))
  206. }
  207. return *NewSockMessage(MsgResponse, REQUEST_OK, []byte("Configuration reloaded successfully."))
  208. }
  209. type ConfigRouter struct {
  210. routes map[Method]func(SockMessage) SockMessage
  211. }
  212. func (c *ConfigRouter) Register(method Method, callable func(SockMessage) SockMessage) {
  213. c.routes[method] = callable
  214. }
  215. func (c *ConfigRouter) Routes() map[Method]func(SockMessage) SockMessage {
  216. return c.routes
  217. }
  218. func NewConfigRouter() *ConfigRouter {
  219. return &ConfigRouter{routes: map[Method]func(SockMessage) SockMessage{}}
  220. }
  221. type Configuration struct {
  222. stream io.Writer
  223. Cloud cloudConfig `json:"cloud"`
  224. Ansible ansibleConfig `json:"ansible"`
  225. Service serviceConfig `json:"service"`
  226. HostInfo hostInfo `json:"host_info"`
  227. }
  228. type hostInfo struct {
  229. WireguardSavePath string `json:"wireguard_save_path"`
  230. }
  231. type ansibleConfig struct {
  232. Repo string `json:"repo_url"`
  233. Branch string `json:"branch"`
  234. PlaybookName string `json:"playbook_name"`
  235. }
  236. type serviceConfig struct {
  237. Servers map[string]VpnServer `json:"servers"`
  238. Clients map[string]VpnClient `json:"clients"`
  239. VpnAddressSpace net.IPNet `json:"vpn_address_space"`
  240. VpnAddresses map[string]bool `json:"vpn_addresses"` // Each key is a IPv4 in the VPN, and its corresponding value is what denotes if its in use or not. False == 'In use', True == 'available'
  241. VpnMask int `json:"vpn_mask"` // The mask of the VPN
  242. VpnServerPort int `json:"vpn_server_port"`
  243. SecretsBackend string `json:"secrets_backend"`
  244. SecretsBackendUrl string `json:"secrets_backend_url"`
  245. AnsibleBackend string `json:"ansible_backend"`
  246. AnsibleBackendUrl string `json:"ansible_backend_url"`
  247. }
  248. func (c *Configuration) GetServer(name string) (VpnServer, error) {
  249. server, ok := c.Service.Servers[name]
  250. if ok {
  251. return server, nil
  252. }
  253. for _, server := range c.Service.Servers {
  254. if server.Name == name {
  255. return server, nil
  256. }
  257. }
  258. return VpnServer{}, &ServerNotFound{}
  259. }
  260. func (c *Configuration) GetClient(name string) (VpnClient, error) {
  261. client, ok := c.Service.Clients[name]
  262. if ok {
  263. return client, nil
  264. }
  265. for _, client := range c.Service.Clients {
  266. if client.Name == name {
  267. return client, nil
  268. }
  269. }
  270. return VpnClient{}, &ServerNotFound{}
  271. }
  272. /*
  273. Add a VPN server to the Service configuration
  274. :param server: a VpnServer struct modeling the data that comprises of a VPN server
  275. */
  276. func (c *Configuration) AddServer(addr net.IP, name string, wan string, port int) string {
  277. server, ok := c.Service.Servers[name]
  278. var serverLabel string
  279. if ok {
  280. serverLabel = c.resolveName(server.Name, name)
  281. } else {
  282. serverLabel = name
  283. }
  284. c.Service.Servers[serverLabel] = VpnServer{Name: serverLabel, WanIpv4: wan, VpnIpv4: addr, Port: port}
  285. return serverLabel
  286. }
  287. type VpnClient struct {
  288. Name string `json:"name"`
  289. VpnIpv4 net.IP
  290. Pubkey string `json:"pubkey"`
  291. Default bool `json:"default"`
  292. }
  293. type VpnServer struct {
  294. Name string `json:"name"` // this Label is what is used to index that server and its data within the Daemons model of the VPN environment
  295. WanIpv4 string `json:"wan_ipv4"` // Public IPv4
  296. VpnIpv4 net.IP // the IP address that the server will occupy on the network
  297. Port int
  298. }
  299. /*
  300. Retrieve an available IPv4 from the VPN's set address space. Returns an error if an internal address cant be
  301. parsed to a valid IPv4, or if there are no available addresses left.
  302. */
  303. func (c *Configuration) GetAvailableVpnIpv4() (net.IP, error) {
  304. for addr, used := range c.Service.VpnAddresses {
  305. if !used {
  306. parsedAddr := net.ParseIP(addr)
  307. if parsedAddr == nil {
  308. return nil, &VpnAddressSpaceError{Msg: "Address: " + addr + " couldnt be parsed into a valid IPv4"}
  309. }
  310. c.Service.VpnAddresses[parsedAddr.String()] = true
  311. return parsedAddr, nil
  312. }
  313. }
  314. return nil, &VpnAddressSpaceError{Msg: "No open addresses available in the current VPN address space!"}
  315. }
  316. /*
  317. Return all of the clients from the client list
  318. */
  319. func (c *Configuration) VpnClients() []VpnClient {
  320. clients := []VpnClient{}
  321. for _, val := range c.Service.Clients {
  322. clients = append(clients, val)
  323. }
  324. return clients
  325. }
  326. /*
  327. Get the default VPN client
  328. */
  329. func (c *Configuration) DefaultClient() (VpnClient, error) {
  330. for name := range c.Service.Clients {
  331. if c.Service.Clients[name].Default {
  332. return c.Service.Clients[name], nil
  333. }
  334. }
  335. return VpnClient{}, &ConfigError{Msg: "No default client was specified!"}
  336. }
  337. /*
  338. resolve naming collision in the client list
  339. :param existingName: the name of the existing client in the client list
  340. :param dupeName: the desired name of the client to be added
  341. */
  342. func (c *Configuration) resolveName(existingName string, dupeName string) string {
  343. incr, err := strconv.Atoi(strings.Trim(existingName, dupeName))
  344. if err != nil {
  345. c.Log("Name: ", existingName, "in the client list broke naming convention.")
  346. return dupeName + "0"
  347. }
  348. return fmt.Sprintf("%s%v", dupeName, incr+1)
  349. }
  350. /*
  351. Register a client as a VPN client. This configuration will be propogated into server configs, so that they may connect
  352. :param addr: a net.IP gotten from GetAvailableVpnIpv4()
  353. :param pubkey: the Wireguard public key
  354. :param name: the name/label of this client
  355. */
  356. func (c *Configuration) AddClient(addr net.IP, pubkey string, name string) string {
  357. client, ok := c.Service.Clients[name]
  358. var clientLabel string
  359. if ok {
  360. clientLabel = c.resolveName(client.Name, name)
  361. } else {
  362. clientLabel = name
  363. }
  364. c.Service.Clients[name] = VpnClient{Name: clientLabel, Pubkey: pubkey, VpnIpv4: addr}
  365. return clientLabel
  366. }
  367. /*
  368. Frees up an address to be used
  369. */
  370. func (c *Configuration) FreeAddress(addr string) error {
  371. _, ok := c.Service.VpnAddresses[addr]
  372. if !ok {
  373. return &VpnAddressSpaceError{Msg: "Address: " + addr + " is not in the designated VPN Address space."}
  374. }
  375. c.Service.VpnAddresses[addr] = false
  376. return nil
  377. }
  378. type VpnAddressSpaceError struct {
  379. Msg string
  380. }
  381. func (v *VpnAddressSpaceError) Error() string {
  382. return v.Msg
  383. }
  384. type ServerNotFound struct{}
  385. func (s *ServerNotFound) Error() string { return "Server with the priority passed was not found." }
  386. func (c *Configuration) SetRepo(val string) { c.Ansible.Repo = val }
  387. func (c *Configuration) SetBranch(val string) { c.Ansible.Branch = val }
  388. func (c *Configuration) SetPlaybookName(val string) { c.Ansible.PlaybookName = val }
  389. func (c *Configuration) SetImage(val string) { c.Cloud.Image = val }
  390. func (c *Configuration) SetRegion(val string) { c.Cloud.Region = val }
  391. func (c *Configuration) SetLinodeType(val string) { c.Cloud.LinodeType = val }
  392. func (c *Configuration) SetSecretsBackend(val string) { c.Service.SecretsBackend = val }
  393. func (c *Configuration) SetSecretsBackendUrl(val string) { c.Service.SecretsBackendUrl = val }
  394. func (c *Configuration) Repo() string {
  395. return c.Ansible.Repo
  396. }
  397. func (c *Configuration) Branch() string {
  398. return c.Ansible.Branch
  399. }
  400. func (c *Configuration) PlaybookName() string { return c.Ansible.PlaybookName }
  401. type cloudConfig struct {
  402. Image string `json:"image"`
  403. Region string `json:"region"`
  404. LinodeType string `json:"linode_type"`
  405. }
  406. func (c *Configuration) Image() string {
  407. return c.Cloud.Image
  408. }
  409. func (c *Configuration) Region() string {
  410. return c.Cloud.Region
  411. }
  412. func (c *Configuration) LinodeType() string {
  413. return c.Cloud.LinodeType
  414. }
  415. func (c *Configuration) VpnServerPort() int {
  416. return c.Service.VpnServerPort
  417. }
  418. func (c *Configuration) SecretsBackend() string {
  419. return c.Service.SecretsBackend
  420. }
  421. func (c *Configuration) SecretsBackendUrl() string {
  422. return c.Service.SecretsBackendUrl
  423. }
  424. /*
  425. Log a message to the Contexts 'stream' io.Writer interface
  426. */
  427. func (c *Configuration) Log(data ...string) {
  428. c.stream.Write([]byte(fmt.Sprintf(LogMsgTmpl, time.Now().String(), data)))
  429. }
  430. type ConfigurationBuilder struct {
  431. fileLocations []string
  432. }
  433. /*
  434. Walk through all of the possible configuration avenues, and build out the configuration.
  435. */
  436. func (c ConfigurationBuilder) Build() *Configuration { return nil }
  437. func (c ConfigurationBuilder) readEnv() {}
  438. func (c ConfigurationBuilder) readFiles() {
  439. }
  440. func (c ConfigurationBuilder) readServer() {}
  441. func ReadConfig(path string) *Configuration {
  442. b, err := os.ReadFile(path)
  443. if err != nil {
  444. log.Fatal(err)
  445. }
  446. config := &Configuration{
  447. stream: os.Stdout,
  448. Service: serviceConfig{
  449. Clients: map[string]VpnClient{},
  450. Servers: map[string]VpnServer{},
  451. },
  452. }
  453. err = json.Unmarshal(b, config)
  454. if err != nil {
  455. log.Fatal(err)
  456. }
  457. mask, _ := config.Service.VpnAddressSpace.Mask.Size()
  458. vpnNetwork := fmt.Sprintf("%s/%v", config.Service.VpnAddressSpace.IP.String(), mask)
  459. addresses, err := GetNetworkAddresses(vpnNetwork)
  460. if err != nil {
  461. log.Fatal(err)
  462. }
  463. _, ntwrk, _ := net.ParseCIDR(vpnNetwork)
  464. if config.Service.VpnAddresses == nil {
  465. addrSpace := map[string]bool{}
  466. for i := range addresses.Ipv4s {
  467. addrSpace[addresses.Ipv4s[i].String()] = false
  468. }
  469. config.Service.VpnAddresses = addrSpace
  470. }
  471. config.Service.VpnAddressSpace = *ntwrk
  472. config.Service.VpnMask = addresses.Mask
  473. return config
  474. }
  475. func (c *Configuration) Save(path string) error {
  476. b, err := json.MarshalIndent(c, " ", " ")
  477. if err != nil {
  478. return err
  479. }
  480. return os.WriteFile(path, b, 0666)
  481. }
  482. /*
  483. Create a new Configuration struct with initialized maps
  484. */
  485. func NewConfiguration() *Configuration {
  486. return &Configuration{Service: serviceConfig{Servers: map[string]VpnServer{}, Clients: map[string]VpnClient{}}}
  487. }
  488. func BlankConfig(path string) error {
  489. config := NewConfiguration()
  490. b, err := json.Marshal(config)
  491. if err != nil {
  492. return err
  493. }
  494. os.WriteFile(path, b, 0666)
  495. return nil
  496. }
  497. type ConfigError struct {
  498. Msg string
  499. }
  500. func (c *ConfigError) Error() string {
  501. return "There was an error with the configuration: " + c.Msg
  502. }
  503. /*
  504. ###############################################################
  505. ########### section for the address space functions ###########
  506. ###############################################################
  507. */
  508. type NetworkInterfaceNotFound struct{ Passed string }
  509. // Implementing error interface
  510. func (n *NetworkInterfaceNotFound) Error() string {
  511. return fmt.Sprintf("Interface: '%s' not found.", n.Passed)
  512. }
  513. type IpSubnetMapper struct {
  514. Ipv4s []net.IP `json:"addresses"`
  515. NetworkAddr net.IP
  516. Current net.IP
  517. Mask int
  518. }
  519. /*
  520. Get the next IPv4 address of the address specified in the 'addr' argument,
  521. :param addr: the address to get the next address of
  522. */
  523. func getNextAddr(addr string) string {
  524. parsed, err := netip.ParseAddr(addr)
  525. if err != nil {
  526. log.Fatal("failed while parsing address in getNextAddr() ", err, "\n")
  527. }
  528. return parsed.Next().String()
  529. }
  530. /*
  531. get the network address of the ip address in 'addr' with the subnet mask from 'cidr'
  532. :param addr: the ipv4 address to get the network address of
  533. :param cidr: the CIDR notation of the subbet
  534. */
  535. func getNetwork(addr string, cidr int) string {
  536. addr = fmt.Sprintf("%s/%v", addr, cidr)
  537. ip, net, err := net.ParseCIDR(addr)
  538. if err != nil {
  539. log.Fatal("failed whilst attempting to parse cidr in getNetwork() ", err, "\n")
  540. }
  541. return ip.Mask(net.Mask).String()
  542. }
  543. /*
  544. Recursive function to get all of the IPv4 addresses for each IPv4 network that the host is on
  545. :param ipmap: a pointer to an IpSubnetMapper struct which contains domain details such as
  546. the subnet mask, the original network mask, and the current IP address used in the
  547. recursive function
  548. :param max: This is safety feature to prevent stack overflows, so you can manually set the depth to
  549. call the function
  550. */
  551. func addressRecurse(ipmap *IpSubnetMapper) {
  552. next := getNextAddr(ipmap.Current.String())
  553. nextNet := getNetwork(next, ipmap.Mask)
  554. currentNet := ipmap.NetworkAddr.String()
  555. if nextNet != currentNet {
  556. return
  557. }
  558. ipmap.Current = net.ParseIP(next)
  559. ipmap.Ipv4s = append(ipmap.Ipv4s, net.ParseIP(next))
  560. addressRecurse(ipmap)
  561. }
  562. /*
  563. Get all of the IPv4 addresses in the network that 'addr' belongs to. YOU MUST PASS THE ADDRESS WITH CIDR NOTATION
  564. i.e. '192.168.50.1/24'
  565. :param addr: the ipv4 address to use for subnet discovery
  566. */
  567. func GetNetworkAddresses(addr string) (*IpSubnetMapper, error) {
  568. ipmap := &IpSubnetMapper{Ipv4s: []net.IP{}}
  569. ip, net, err := net.ParseCIDR(addr)
  570. if err != nil {
  571. return nil, err
  572. }
  573. mask, err := strconv.Atoi(strings.Split(addr, "/")[1])
  574. if err != nil {
  575. return nil, err
  576. }
  577. ipmap.NetworkAddr = ip.Mask(net.Mask)
  578. ipmap.Mask = mask
  579. ipmap.Current = ip.Mask(net.Mask)
  580. addressRecurse(ipmap)
  581. return ipmap, nil
  582. }