VOL-1430, SEBA-436 Add EAPOL responder
Change-Id: I9b01271373099a843ada51fdf0a8639abaf8667e
diff --git a/core/eapol.go b/core/eapol.go
new file mode 100644
index 0000000..e40da47
--- /dev/null
+++ b/core/eapol.go
@@ -0,0 +1,257 @@
+/*
+ * 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"
+ "gerrit.opencord.org/voltha-bbsim/common/logger"
+ "net"
+ "errors"
+ "encoding/hex"
+ "github.com/google/gopacket"
+ "github.com/google/gopacket/layers"
+ "crypto/md5"
+ "sync"
+)
+
+type eapState int
+const (
+ START eapState = iota //TODO: This state definition should support 802.1X
+ RESPID
+ RESPCHA
+ SUCCESS
+)
+
+
+
+type responder struct {
+ peers map [key] *peerInstance
+ eapolIn chan *EAPByte
+}
+
+type peerInstance struct{
+ key key
+ srcaddr *net.HardwareAddr
+ version uint8
+ curId uint8
+ curState eapState
+}
+
+type key struct {
+ intfid uint32
+ onuid uint32
+}
+
+var resp *responder
+var once sync.Once
+func getResponder () *responder {
+ once.Do(func(){
+ resp = &responder{peers: make(map[key] *peerInstance), eapolIn: nil}
+ })
+ return resp
+}
+
+func RunEapolResponder(ctx context.Context, eapolOut chan *EAPPkt, eapolIn chan *EAPByte, errch chan error) {
+ responder := getResponder()
+ responder.eapolIn = eapolIn
+ peers := responder.peers
+
+ go func() {
+ logger.Debug("EAPOL response process starts")
+ defer logger.Debug("EAPOL response process was done")
+ for {
+ select {
+ case msg := <- eapolOut:
+ logger.Debug("Received eapol from eapolOut")
+ intfid := msg.IntfId
+ onuid := msg.OnuId
+
+ if peer, ok := peers[key{intfid: intfid, onuid:onuid}]; ok {
+ logger.Debug("Key hit intfid:%d onuid: %d", intfid, onuid)
+ curstate := peer.curState
+ nextstate, err := peer.transitState(curstate, eapolIn, msg.Pkt)
+ if err != nil {
+ logger.Error("Failed to transitState: %s", err)
+ }
+ peer.curState = nextstate
+ } else {
+ logger.Error("Failed to find eapol peer instance intfid:%d onuid:%d", intfid, onuid)
+ }
+ case <-ctx.Done():
+ return
+ }
+ }
+ }()
+}
+
+func startPeer (intfid uint32, onuid uint32) error {
+ peer := peerInstance{key: key{intfid: intfid, onuid: onuid},
+ srcaddr: &net.HardwareAddr{0x2e, 0x60, 0x70, 0x13, 0x07, byte(onuid)},
+ version: 1,
+ curId: 0,
+ curState: START}
+
+ eap := peer.createEAPStart()
+ bytes := peer.createEAPOL(eap)
+ resp := getResponder()
+ eapolIn := resp.eapolIn
+ if err := peer.sendPkt(bytes, eapolIn); err != nil {
+ return errors.New("Failed to send EAPStart")
+ }
+ logger.Debug("Sending EAPStart")
+ logger.Debug(hex.Dump(bytes))
+ //peers[key{intfid: intfid, onuid: onuid}] = &peer
+ resp.peers[key{intfid: intfid, onuid: onuid}] = &peer
+ return nil
+}
+
+func (p *peerInstance)transitState(cur eapState, omciIn chan *EAPByte, recvpkt gopacket.Packet) (next eapState, err error) {
+ logger.Debug("currentState:%d", cur)
+ eap, err := extractEAP(recvpkt)
+ if err != nil {
+ return cur, nil
+ }
+ if eap.Code == layers.EAPCodeRequest && eap.Type == layers.EAPTypeIdentity {
+ logger.Debug("Received EAP-Request/Identity")
+ logger.Debug(recvpkt.Dump())
+ p.curId = eap.Id
+ if cur == START {
+ reseap := p.createEAPResID()
+ pkt := p.createEAPOL(reseap)
+ logger.Debug("Sending EAP-Response/Identity")
+ if err != p.sendPkt(pkt, omciIn) {
+ return cur, err
+ }
+ return RESPID, nil
+ }
+ } else if eap.Code == layers.EAPCodeRequest && eap.Type == layers.EAPTypeOTP {
+ logger.Debug("Received EAP-Request/Challenge")
+ logger.Debug(recvpkt.Dump())
+ if cur == RESPID {
+ p.curId = eap.Id
+ resdata := getMD5Res (p.curId, eap)
+ resdata = append([]byte{0x10}, resdata ...)
+ reseap := p.createEAPResCha(resdata)
+ pkt := p.createEAPOL(reseap)
+ logger.Debug("Sending EAP-Response/Challenge")
+ if err != p.sendPkt(pkt, omciIn) {
+ return cur, err
+ }
+ return RESPCHA, nil
+ }
+ } else if eap.Code == layers.EAPCodeSuccess && eap.Type == layers.EAPTypeNone {
+ logger.Debug("Received EAP-Success")
+ logger.Debug(recvpkt.Dump())
+ if cur == RESPCHA {
+ return SUCCESS, nil
+ }
+ } else {
+ logger.Debug("Received unsupported EAP")
+ return cur, nil
+ }
+ logger.Debug("State transition does not support..current state:%d", cur)
+ logger.Debug(recvpkt.Dump())
+ return cur, nil
+}
+
+func (p *peerInstance) createEAPOL (eap *layers.EAP) []byte {
+ buffer := gopacket.NewSerializeBuffer()
+ options := gopacket.SerializeOptions{}
+
+ ethernetLayer := &layers.Ethernet{
+ SrcMAC: *p.srcaddr,
+ DstMAC: net.HardwareAddr{0x01, 0x80, 0xC2, 0x00, 0x00, 0x03},
+ EthernetType: layers.EthernetTypeEAPOL,
+ }
+
+ if eap == nil { // EAP Start
+ gopacket.SerializeLayers(buffer, options,
+ ethernetLayer,
+ &layers.EAPOL{Version: p.version, Type:1, Length: 0},
+ )
+ } else {
+ gopacket.SerializeLayers(buffer, options,
+ ethernetLayer,
+ &layers.EAPOL{Version: p.version, Type:0, Length: eap.Length},
+ eap,
+ )
+ }
+ bytes := buffer.Bytes()
+ return bytes
+}
+
+func (p *peerInstance) createEAPStart () *layers.EAP {
+ return nil
+}
+
+func (p *peerInstance) createEAPResID () *layers.EAP {
+ eap := layers.EAP{Code: layers.EAPCodeResponse,
+ Id: p.curId,
+ Length: 9,
+ Type: layers.EAPTypeIdentity,
+ TypeData: []byte{0x75, 0x73, 0x65, 0x72 }}
+ return &eap
+}
+
+func (p *peerInstance) createEAPResCha (payload []byte) *layers.EAP {
+ eap := layers.EAP{Code: layers.EAPCodeResponse,
+ Id: p.curId, Length: 22,
+ Type: layers.EAPTypeOTP,
+ TypeData: payload}
+ return &eap
+}
+
+func (p *peerInstance) sendPkt (pkt []byte, omciIn chan *EAPByte) error {
+ // Send our packet
+ msg := EAPByte{IntfId: p.key.intfid,
+ OnuId: p.key.onuid,
+ Byte: pkt}
+ omciIn <- &msg
+ logger.Debug("sendPkt intfid:%d onuid:%d", p.key.intfid, p.key.onuid)
+ logger.Debug(hex.Dump(msg.Byte))
+ return nil
+}
+
+func getMD5Res (id uint8, eap *layers.EAP) []byte {
+ i := byte(id)
+ C := []byte(eap.BaseLayer.Contents)[6:]
+ P := []byte{i, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64} //"password"
+ data := md5.Sum(append(P, C ...))
+ ret := make([]byte, 16)
+ for j := 0; j < 16; j ++ {
+ ret[j] = data[j]
+ }
+ return ret
+}
+
+func extractEAPOL (pkt gopacket.Packet) (*layers.EAPOL, error) {
+ layerEAPOL := pkt.Layer(layers.LayerTypeEAPOL)
+ eapol, _ := layerEAPOL.(*layers.EAPOL)
+ if eapol == nil {
+ return nil, errors.New("Cannot extract EAPOL")
+ }
+ return eapol, nil
+}
+
+func extractEAP (pkt gopacket.Packet) (*layers.EAP, error) {
+ layerEAP := pkt.Layer(layers.LayerTypeEAP)
+ eap, _ := layerEAP.(*layers.EAP)
+ if eap == nil {
+ return nil, errors.New("Cannot extract EAP")
+ }
+ return eap, nil
+}
\ No newline at end of file