{"action":"created","issue":{"url":"https://api.github.com/repos/u-root/u-root/issues/663","repository_url":"https://api.github.com/repos/u-root/u-root","labels_url":"https://api.github.com/repos/u-root/u-root/issues/663/labels{/name}","comments_url":"https://api.github.com/repos/u-root/u-root/issues/663/comments","events_url":"https://api.github.com/repos/u-root/u-root/issues/663/events","html_url":"https://github.com/u-root/u-root/issues/663","id":301982531,"number":663,"title":"dhclient: parallelize","user":{"login":"hugelgupf","id":1994130,"avatar_url":"https://avatars2.githubusercontent.com/u/1994130?v=4","gravatar_id":"","url":"https://api.github.com/users/hugelgupf","html_url":"https://github.com/hugelgupf","followers_url":"https://api.github.com/users/hugelgupf/followers","following_url":"https://api.github.com/users/hugelgupf/following{/other_user}","gists_url":"https://api.github.com/users/hugelgupf/gists{/gist_id}","starred_url":"https://api.github.com/users/hugelgupf/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/hugelgupf/subscriptions","organizations_url":"https://api.github.com/users/hugelgupf/orgs","repos_url":"https://api.github.com/users/hugelgupf/repos","events_url":"https://api.github.com/users/hugelgupf/events{/privacy}","received_events_url":"https://api.github.com/users/hugelgupf/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":0,"created_at":"2018-03-03T09:06:30Z","updated_at":"2018-03-03T09:17:39Z","closed_at":null,"author_association":"OWNER","body":"Now that DHCPv4 and DHCPv6 clients have `SendAndRead`, this is the intention for parallelizing DHCP client requests on different NICs:\r\n\r\n```go\r\nv4packets := make(chan *dhcp4client.ClientPacket, ..)\r\nv6packets := ...\r\nv4errs := make(chan *dhcp4client.ClientError, ...)\r\nv6errs := ...\r\n\r\nctx, cancel := context.WithTimeout(ctx, 30 * time.Second)\r\ndefer cancel()\r\n\r\nfor _, nic := range nics {\r\n  c4 := dhcp4client.New(nic, ...)\r\n  p4 := c4.DiscoverPacket()\r\n  go c4.SendAndRead(ctx, dhcp4client.DefaultServers, p4, v4packets, v4errs)\r\n\r\n  c6 := ...\r\n  p6 := c6.SolicitPacket() \r\n  go c6.SendAndRead(ctx, dhcp6client.DefaultServers, p6, v6packets, v6errs)\r\n}\r\n\r\nfor {\r\n  select {\r\n  case p6, ok := <-v6packets:\r\n    // Handle DHCPv6 packet: p6.Packet came on interface p6.Interface.\r\n    // Must verify that the packet is something you expect, like a DHCP response WITH a boot file name!\r\n    if isAdvertisePacket(p6) {\r\n      // Complete 4-way handshake.\r\n      request := getRequestPacketFromAdvertise(p6)\r\n      // TODO: add *Client to ClientError and ClientPacket types, so you can use it here as p6.Client.\r\n      go p6.Client.SendAndRead(ctx, dhcp6client.DefaultServers, request, v6packets, v6errs)\r\n    }\r\n\r\n  case p4, ok := <-v4packets:\r\n    // Handle DHCPv4 packet: p4.Packet came on interface p4.Interface.\r\n\r\n  case err, ok := <-v4errs:\r\n    // ...\r\n\r\n  case err, ok := <-v6errs:\r\n    // ...\r\n  }\r\n}\r\n```\r\n\r\nThis is just the rough sketch, but that's roughly what it was intended to be.\r\n\r\nWhen you receive all the packets on channels, you can decide what your *policy* is going to be. Maybe in dhclient, you wait for a packet on all interfaces and configure all interfaces. Maybe in pxeboot, you are satisfied using the first valid packet you get. \r\n\r\nIf you decide that packet X is THE packet, call `cancel`. That will shut down all goroutines still waiting for responses. The ideal way to use all this is to encapsulate the choosing of the packet, if you are going to choose, in a function:\r\n\r\n```go\r\nfunc getPacket(nics ...) {\r\n  ctx, cancel := context.WithTimeout(ctx, 30 * time.Second)\r\n  defer cancel()\r\n\r\n  // ... \r\n  go c.SendAndRead(ctx, ...)\r\n  // ...\r\n\r\n  for {\r\n    select {\r\n    case p, err := <-responsePackets:\r\n      if hasAttributesIWant(p) {\r\n        // defer'd cancel will shut down all remaining goroutines!\r\n        return p\r\n      }\r\n    }\r\n  }\r\n}\r\n```"},"comment":{"url":"https://api.github.com/repos/u-root/u-root/issues/comments/370133305","html_url":"https://github.com/u-root/u-root/issues/663#issuecomment-370133305","issue_url":"https://api.github.com/repos/u-root/u-root/issues/663","id":370133305,"user":{"login":"hugelgupf","id":1994130,"avatar_url":"https://avatars2.githubusercontent.com/u/1994130?v=4","gravatar_id":"","url":"https://api.github.com/users/hugelgupf","html_url":"https://github.com/hugelgupf","followers_url":"https://api.github.com/users/hugelgupf/followers","following_url":"https://api.github.com/users/hugelgupf/following{/other_user}","gists_url":"https://api.github.com/users/hugelgupf/gists{/gist_id}","starred_url":"https://api.github.com/users/hugelgupf/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/hugelgupf/subscriptions","organizations_url":"https://api.github.com/users/hugelgupf/orgs","repos_url":"https://api.github.com/users/hugelgupf/repos","events_url":"https://api.github.com/users/hugelgupf/events{/privacy}","received_events_url":"https://api.github.com/users/hugelgupf/received_events","type":"User","site_admin":false},"created_at":"2018-03-03T09:17:39Z","updated_at":"2018-03-03T09:17:39Z","author_association":"OWNER","body":"A low-priority TODO here is that `SendAndRead` in each client shouldn't be spawned as a new goroutine -- per dhcp4client / dhcp6client instance there should be a receiving goroutine. Sending then is adding the transaction ID to a map[XID]response-chan and the receiving goroutine will send any responses with the matching XID on that channel. That'll just reduce churn for this, though (and slightly improve the API)."}}
{"actor":{"display_login":"hugelgupf"}}