VOL-1431, SEBA-436 Add DHCP responder
Change-Id: Ifdc614a8b5a212e23308d4fcc71987b75397fea2
diff --git a/core/core_server.go b/core/core_server.go
index 0f6b8f4..7cc1eed 100644
--- a/core/core_server.go
+++ b/core/core_server.go
@@ -65,6 +65,8 @@
omciOut chan openolt.OmciMsg
eapolIn chan *byteMsg
eapolOut chan *byteMsg
+ dhcpIn chan *byteMsg
+ dhcpOut chan *byteMsg
}
type Packet struct {
@@ -104,6 +106,8 @@
omciOut: make(chan openolt.OmciMsg, 1024),
eapolIn: make(chan *byteMsg, 1024),
eapolOut: make(chan *byteMsg, 1024),
+ dhcpIn: make(chan *byteMsg, 1024),
+ dhcpOut: make(chan *byteMsg, 1024),
}
nnni := s.Olt.NumNniIntf
@@ -356,6 +360,9 @@
errchEapol := make(chan error)
RunEapolResponder(ctx, s.eapolOut, s.eapolIn, errchEapol)
+ errchDhcp := make(chan error)
+ RunDhcpResponder(ctx, s.dhcpOut, s.dhcpIn, errchDhcp)
+
eg.Go(func() error {
logger.Debug("runOMCIResponder Start")
defer logger.Debug("runOMCIResponder Done")
@@ -389,6 +396,21 @@
})
eg.Go(func() error {
+ logger.Debug("runDhcpResponder Start")
+ defer logger.Debug("runDhcpResponder Done")
+ select {
+ case v, ok := <-errchDhcp:
+ if ok { //Error
+ logger.Error("Error happend in Dhcp:%s", v)
+ return v
+ }
+ case <-child.Done():
+ return nil
+ }
+ return nil
+ })
+
+ eg.Go(func() error {
err := s.runMainPktLoop(child, stream)
return err
})
@@ -456,6 +478,49 @@
logger.Error("Fail to send EAPOL PktInd indication.", err)
return err
}
+ case msg := <- s.dhcpIn: //TODO: We should put omciIn, eapolIn, dhcpIn toghether
+ intfid := msg.IntfId
+ onuid := msg.OnuId
+ gemid, err := getGemPortID(intfid, onuid)
+ bytes := msg.Byte
+ pkt := gopacket.NewPacket(bytes, layers.LayerTypeEthernet, gopacket.Default)
+
+ if err != nil {
+ logger.Error("Failed to getGemPortID intfid:%d onuid:%d", intfid, onuid)
+ continue
+ }
+ /*
+ onu, err := s.GetOnuByID(onuid)
+ if err != nil {
+ logger.Error("Failed to GetOnuByID:%d", onuid)
+ continue
+ }
+ sn := convB2S(onu.SerialNumber.VendorSpecific)
+ if ctag, ok := s.CtagMap[sn]; ok == true {
+ tagpkt, err := PushVLAN(pkt, uint16(ctag), onu)
+ if err != nil {
+ utils.LoggerWithOnu(onu).WithFields(log.Fields{
+ "gemId": gemid,
+ }).Error("Fail to tag C-tag")
+ } else {
+ pkt = tagpkt
+ }
+ } else {
+ utils.LoggerWithOnu(onu).WithFields(log.Fields{
+ "gemId": gemid,
+ "cTagMap": s.CtagMap,
+ }).Error("Could not find onuid in CtagMap", onuid, sn, s.CtagMap)
+ }
+ */
+ logger.Debug("OLT %d send dhcp packet in (upstream), IF %v (ONU-ID: %v) pkt:%x.", s.Olt.ID, intfid, onuid)
+ logger.Debug(pkt.Dump())
+
+ data = &openolt.Indication_PktInd{PktInd: &openolt.PacketIndication{IntfType: "pon", IntfId: intfid, GemportId: gemid, Pkt: msg.Byte}}
+ if err := stream.Send(&openolt.Indication{Data: data}); err != nil {
+ logger.Error("Fail to send DHCP PktInd indication.", err)
+ return err
+ }
+
case unipkt := <-unichannel:
onuid := unipkt.Info.onuid
onu, _ := s.GetOnuByID(onuid)
@@ -527,7 +592,6 @@
utils.LoggerWithOnu(onu).Info("Received packet from NNI in grpc Server.")
intfid := nnipkt.Info.intfid
pkt := nnipkt.Pkt
- utils.LoggerWithOnu(onu).Info("sendPktInd - NNI Packet")
data = &openolt.Indication_PktInd{PktInd: &openolt.PacketIndication{IntfType: "nni", IntfId: intfid, Pkt: pkt.Data()}}
if err := stream.Send(&openolt.Indication{Data: data}); err != nil {
logger.Error("Fail to send PktInd indication.", err)
@@ -561,6 +625,10 @@
}).Info("Received downstream packet is DHCP.")
rawpkt, _, _ = PopVLAN(rawpkt)
rawpkt, _, _ = PopVLAN(rawpkt)
+ logger.Debug("%s", rawpkt.Dump())
+ dhcpPkt := byteMsg{IntfId:intfid, OnuId:onuid, Byte: rawpkt.Data()}
+ s.dhcpOut <- &dhcpPkt
+ return nil
} else {
utils.LoggerWithOnu(onu).Info("WARNING: Received packet is not EAPOL or DHCP")
return nil
@@ -589,6 +657,7 @@
return err
}
handle := ioinfo.handler
+ logger.Debug("%s", poppkt.Dump())
SendNni(handle, poppkt)
return nil
}
diff --git a/core/dhcp.go b/core/dhcp.go
new file mode 100644
index 0000000..12b4652
--- /dev/null
+++ b/core/dhcp.go
@@ -0,0 +1,345 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package core
+
+import (
+ "context"
+ "encoding/hex"
+ "errors"
+ "fmt"
+ "gerrit.opencord.org/voltha-bbsim/common/logger"
+ "github.com/google/gopacket"
+ "github.com/google/gopacket/layers"
+ "math/rand"
+ "net"
+ "reflect"
+ "sync"
+)
+
+const (
+ DHCP_INIT clientState = iota + 1
+ DHCP_SELECTING
+ DHCP_REQUESTING
+ DHCP_BOUND
+)
+
+type dhcpResponder struct {
+ clients map[clientKey]*dhcpClientInstance
+ dhcpIn chan *byteMsg
+}
+
+type dhcpClientInstance struct {
+ key clientKey
+ srcaddr *net.HardwareAddr
+ srcIP *net.IPAddr
+ serverIP *net.IPAddr
+ hostname string
+ curId uint32
+ curState clientState
+}
+
+var dhcpresp *dhcpResponder
+var dhcponce sync.Once
+
+func getDHCPResponder() *dhcpResponder {
+ dhcponce.Do(func() {
+ dhcpresp = &dhcpResponder{clients: make(map[clientKey]*dhcpClientInstance), dhcpIn: nil}
+ })
+ return dhcpresp
+}
+
+var defaultParamsRequestList = []layers.DHCPOpt{
+ layers.DHCPOptSubnetMask,
+ layers.DHCPOptBroadcastAddr,
+ layers.DHCPOptTimeOffset,
+ layers.DHCPOptRouter,
+ layers.DHCPOptDomainName,
+ layers.DHCPOptDNS,
+ layers.DHCPOptDomainSearch,
+ layers.DHCPOptHostname,
+ layers.DHCPOptNetBIOSTCPNS,
+ layers.DHCPOptNetBIOSTCPScope,
+ layers.DHCPOptInterfaceMTU,
+ layers.DHCPOptClasslessStaticRoute,
+ layers.DHCPOptNTPServers,
+}
+
+func RunDhcpResponder(ctx context.Context, dhcpOut chan *byteMsg, dhcpIn chan *byteMsg, errch chan error) {
+ responder := getDHCPResponder()
+ responder.dhcpIn = dhcpIn
+ clients := responder.clients
+
+ go func() {
+ logger.Debug("DHCP response process starts")
+ defer logger.Debug("DHCP response process was done")
+ for {
+ select {
+ case msg := <-dhcpOut:
+ logger.Debug("Received dhcp message from dhcpOut")
+
+ if c, ok := clients[clientKey{intfid: msg.IntfId, onuid: msg.OnuId}]; ok {
+ nextstate := respondMessage("DHCP", *c, msg, dhcpIn)
+ c.updateState(nextstate)
+ } else {
+ logger.Error("Failed to find dhcp client instance intfid:%d onuid:%d", msg.IntfId, msg.OnuId)
+ }
+ case <-ctx.Done():
+ return
+ }
+ }
+ }()
+}
+
+func startDHCPClient(intfid uint32, onuid uint32) error {
+ logger.Debug("startDHCPClient")
+ client := dhcpClientInstance{key: clientKey{intfid: intfid, onuid: onuid},
+ srcaddr: &net.HardwareAddr{0x2e, 0x60, 0x70, 0x13, 0x07, byte(onuid)},
+ hostname: "voltha",
+ curId: rand.Uint32(),
+ curState: DHCP_INIT}
+
+ dhcp := client.createDHCPDisc()
+ bytes, err := client.createDHCP(dhcp)
+ if err != nil {
+ logger.Error("%s", err)
+ return err
+ }
+ resp := getDHCPResponder()
+ dhcpIn := resp.dhcpIn
+ if err := client.sendBytes(bytes, dhcpIn); err != nil {
+ logger.Error("Failed to send DHCP Discovery")
+ return errors.New("Failed to send DHCP Discovery")
+ }
+ client.curState = DHCP_SELECTING
+ logger.Debug("Sending DHCP Discovery")
+ resp.clients[clientKey{intfid: intfid, onuid: onuid}] = &client
+ return nil
+}
+
+func (c dhcpClientInstance) transitState(cur clientState, recvbytes []byte) (next clientState, sendbytes []byte, err error) {
+ recvpkt := gopacket.NewPacket(recvbytes, layers.LayerTypeEthernet, gopacket.Default)
+ dhcp, err := extractDHCP(recvpkt)
+ if err != nil {
+ return cur, nil, nil
+ }
+ msgType, err := getMsgType(dhcp)
+ if err != nil {
+ logger.Error("%s", err)
+ return cur, nil, nil
+ }
+ if dhcp.Operation == layers.DHCPOpReply && msgType == layers.DHCPMsgTypeOffer {
+ logger.Debug("Received DHCP Offer")
+ logger.Debug(recvpkt.Dump())
+ if cur == DHCP_SELECTING {
+ senddhcp := c.createDHCPReq()
+ sendbytes, err := c.createDHCP(senddhcp)
+ if err != nil {
+ logger.Debug("Failed to createDHCP")
+ return cur, nil, err
+ }
+ return DHCP_REQUESTING, sendbytes, nil
+ }
+ } else if dhcp.Operation == layers.DHCPOpReply && msgType == layers.DHCPMsgTypeAck {
+ logger.Debug("Received DHCP Ack")
+ logger.Debug(recvpkt.Dump())
+ if cur == DHCP_REQUESTING {
+ return DHCP_BOUND, nil, nil
+ }
+ } else if dhcp.Operation == layers.DHCPOpReply && msgType == layers.DHCPMsgTypeRelease {
+ if cur == DHCP_BOUND {
+ senddhcp := c.createDHCPDisc()
+ sendbytes, err := c.createDHCP(senddhcp)
+ if err != nil {
+ fmt.Println("Failed to createDHCP")
+ return DHCP_INIT, nil, err
+ }
+ return DHCP_SELECTING, sendbytes, nil
+ }
+ } else {
+ logger.Debug("Received unsupported DHCP message Operation:%d MsgType:%d", dhcp.Operation, msgType)
+ return cur, nil, nil
+ }
+ logger.Error("State transition does not support..current state:%d", cur)
+ logger.Debug(recvpkt.Dump())
+
+ return cur, nil, nil
+}
+
+func (c dhcpClientInstance) getState() clientState {
+ return c.curState
+}
+
+func (c *dhcpClientInstance) updateState(state clientState) {
+ msg := fmt.Sprintf("DHCP update state intfid:%d onuid:%d state:%d", c.key.intfid, c.key.onuid, state)
+ logger.Debug(msg)
+ c.curState = state
+}
+
+func (c dhcpClientInstance) getKey() clientKey {
+ return c.key
+}
+
+func (c *dhcpClientInstance) createDHCP(dhcp *layers.DHCPv4) ([]byte, error) {
+ buffer := gopacket.NewSerializeBuffer()
+ options := gopacket.SerializeOptions{
+ ComputeChecksums: true,
+ FixLengths: true,
+ }
+ ethernetLayer := &layers.Ethernet{
+ SrcMAC: *c.srcaddr,
+ DstMAC: net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ EthernetType: layers.EthernetTypeIPv4,
+ }
+
+ ipLayer := &layers.IPv4{
+ Version: 4,
+ TOS: 0x10,
+ TTL: 128,
+ SrcIP: []byte{0, 0, 0, 0},
+ DstIP: []byte{255, 255, 255, 255},
+ Protocol: layers.IPProtocolUDP,
+ }
+
+ udpLayer := &layers.UDP{
+ SrcPort: 68,
+ DstPort: 67,
+ }
+
+ udpLayer.SetNetworkLayerForChecksum(ipLayer)
+ if err := gopacket.SerializeLayers(buffer, options, ethernetLayer, ipLayer, udpLayer, dhcp); err != nil {
+ return nil, err
+ }
+
+ bytes := buffer.Bytes()
+ return bytes, nil
+}
+
+func (c *dhcpClientInstance) createDefaultDHCPReq() layers.DHCPv4 {
+ return layers.DHCPv4{
+ Operation: layers.DHCPOpRequest,
+ HardwareType: layers.LinkTypeEthernet,
+ HardwareLen: 6,
+ HardwareOpts: 0,
+ Xid: c.curId,
+ ClientHWAddr: *c.srcaddr,
+ }
+}
+
+func (c *dhcpClientInstance) createDefaultOpts() []layers.DHCPOption {
+ hostname := []byte(c.hostname)
+ opts := []layers.DHCPOption{}
+ opts = append(opts, layers.DHCPOption{
+ Type: layers.DHCPOptHostname,
+ Data: hostname,
+ Length: uint8(len(hostname)),
+ })
+
+ bytes := []byte{}
+ for _, option := range defaultParamsRequestList {
+ bytes = append(bytes, byte(option))
+ }
+
+ opts = append(opts, layers.DHCPOption{
+ Type: layers.DHCPOptParamsRequest,
+ Data: bytes,
+ Length: uint8(len(bytes)),
+ })
+ return opts
+}
+
+func (c *dhcpClientInstance) createDHCPDisc() *layers.DHCPv4 {
+ dhcpLayer := c.createDefaultDHCPReq()
+ defaultOpts := c.createDefaultOpts()
+ dhcpLayer.Options = append([]layers.DHCPOption{layers.DHCPOption{
+ Type: layers.DHCPOptMessageType,
+ Data: []byte{byte(layers.DHCPMsgTypeDiscover)},
+ Length: 1,
+ }}, defaultOpts...)
+
+ return &dhcpLayer
+}
+
+func (c *dhcpClientInstance) createDHCPReq() *layers.DHCPv4 {
+ dhcpLayer := c.createDefaultDHCPReq()
+ defaultOpts := c.createDefaultOpts()
+ dhcpLayer.Options = append(defaultOpts, layers.DHCPOption{
+ Type: layers.DHCPOptMessageType,
+ Data: []byte{byte(layers.DHCPMsgTypeRequest)},
+ Length: 1,
+ })
+
+ data := []byte{182, 21, 0, 128}
+ dhcpLayer.Options = append(dhcpLayer.Options, layers.DHCPOption{
+ Type: layers.DHCPOptServerID,
+ Data: data,
+ Length: uint8(len(data)),
+ })
+
+ data = []byte{0xcd, 0x28, 0xcb, 0xcc, 0x00, 0x01, 0x00, 0x01,
+ 0x23, 0xed, 0x11, 0xec, 0x4e, 0xfc, 0xcd, 0x28, 0xcb, 0xcc}
+ dhcpLayer.Options = append(dhcpLayer.Options, layers.DHCPOption{
+ Type: layers.DHCPOptClientID,
+ Data: data,
+ Length: uint8(len(data)),
+ })
+
+ data = []byte{182, 21, 0, byte(c.key.onuid)}
+ dhcpLayer.Options = append(dhcpLayer.Options, layers.DHCPOption{
+ Type: layers.DHCPOptRequestIP,
+ Data: data,
+ Length: uint8(len(data)),
+ })
+ return &dhcpLayer
+}
+
+func (c *dhcpClientInstance) sendBytes(bytes []byte, dhcpIn chan *byteMsg) error {
+ // Send our packet
+ msg := byteMsg{IntfId: c.key.intfid,
+ OnuId: c.key.onuid,
+ Byte: bytes}
+ dhcpIn <- &msg
+ logger.Debug("sendBytes intfid:%d onuid:%d", c.key.intfid, c.key.onuid)
+ logger.Debug(hex.Dump(msg.Byte))
+ return nil
+}
+
+func extractDHCP(pkt gopacket.Packet) (*layers.DHCPv4, error) {
+ layerDHCP := pkt.Layer(layers.LayerTypeDHCPv4)
+ dhcp, _ := layerDHCP.(*layers.DHCPv4)
+ if dhcp == nil {
+ return nil, errors.New("Failed to extract DHCP")
+ }
+ return dhcp, nil
+}
+
+func getMsgType(dhcp *layers.DHCPv4) (layers.DHCPMsgType, error) {
+ for _, option := range dhcp.Options {
+ if option.Type == layers.DHCPOptMessageType {
+ if reflect.DeepEqual(option.Data, []byte{byte(layers.DHCPMsgTypeOffer)}) {
+ return layers.DHCPMsgTypeOffer, nil
+ } else if reflect.DeepEqual(option.Data, []byte{byte(layers.DHCPMsgTypeAck)}) {
+ return layers.DHCPMsgTypeAck, nil
+ } else if reflect.DeepEqual(option.Data, []byte{byte(layers.DHCPMsgTypeRelease)}) {
+ return layers.DHCPMsgTypeRelease, nil
+ } else {
+ msg := fmt.Sprintf("This type %x is not supported", option.Data)
+ return 0, errors.New(msg)
+ }
+ }
+ }
+ return 0, errors.New("Failed to extract MsgType from dhcp")
+}
diff --git a/core/mediator.go b/core/mediator.go
index 203204f..aea0e71 100644
--- a/core/mediator.go
+++ b/core/mediator.go
@@ -56,7 +56,7 @@
modeopt := flag.String("m", "default", "Emulation mode (default, aaa, both (aaa & dhcp))")
aaawait := flag.Int("aw", 10, "Wait time (sec) for activation WPA supplicants")
dhcpwait := flag.Int("dw", 20, "Wait time (sec) for activation DHCP clients")
- dhcpservip := flag.String("s", "182.21.0.1", "DHCP Server IP Address")
+ dhcpservip := flag.String("s", "182.21.0.128", "DHCP Server IP Address")
intvl := flag.Int("v", 1, "Interval each Indication")
intvl_test := flag.Int("V", 1, "Interval each Indication")
kafkaBroker := flag.String("k", "", "Kafka broker")
diff --git a/core/openolt_service.go b/core/openolt_service.go
index 364d0c3..4b13f3e 100644
--- a/core/openolt_service.go
+++ b/core/openolt_service.go
@@ -17,12 +17,11 @@
package core
import (
- "time"
-
"gerrit.opencord.org/voltha-bbsim/common/logger"
"gerrit.opencord.org/voltha-bbsim/common/utils"
"gerrit.opencord.org/voltha-bbsim/device"
"gerrit.opencord.org/voltha-bbsim/protos"
+ "time"
)
func sendOltIndUp(stream openolt.Openolt_EnableIndicationServer, olt *device.Olt) error {
diff --git a/core/tester.go b/core/tester.go
index e5f9aa8..5ca3e5a 100644
--- a/core/tester.go
+++ b/core/tester.go
@@ -312,11 +312,15 @@
func activateDHCPClient(univeth UniVeth, s *Server) (err error) {
onu, _ := s.GetOnuByID(univeth.OnuId)
+ if err = startDHCPClient(onu.IntfID, onu.OnuID); err != nil {
+ logger.Error("%s", err)
+ }
+ /*
cmd := exec.Command("/usr/local/bin/dhclient", univeth.Veth)
if err := cmd.Start(); err != nil {
logger.Error("Fail to activateDHCPClient() for: %s", univeth.Veth)
logger.Panic("activateDHCPClient %s", err)
- }
+ }*/
utils.LoggerWithOnu(onu).WithFields(log.Fields{
"veth": univeth.Veth,
@@ -324,7 +328,7 @@
return
}
-func activateDHCPServer(veth string, serverip string) error {
+func activateDHCPServer (veth string, serverip string) error {
err := exec.Command("ip", "addr", "add", serverip, "dev", veth).Run()
if err != nil {
logger.Error("Fail to add ip to %s address: %s", veth, err)
@@ -337,7 +341,8 @@
}
cmd := "/usr/local/bin/dhcpd"
conf := "/etc/dhcp/dhcpd.conf"
- err = exec.Command(cmd, "-cf", conf, veth).Run()
+ logfile := "/tmp/dhcplog"
+ err = exec.Command(cmd, "-cf", conf, veth, "-tf", logfile).Run()
if err != nil {
logger.Error("Fail to activateDHCP Server (): %s", err)
return err