blob: 05585fc18721e8779b02784e40583bd5fc46c9af [file] [log] [blame]
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -07001/*
2 * Copyright 2018-present Open Networking Foundation
3
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7
8 * http://www.apache.org/licenses/LICENSE-2.0
9
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package devices
18
19import (
20 "bytes"
21 "github.com/google/gopacket"
22 "github.com/google/gopacket/pcap"
23 "github.com/looplab/fsm"
24 "github.com/opencord/bbsim/internal/bbsim/packetHandlers"
25 "github.com/opencord/bbsim/internal/bbsim/types"
26 log "github.com/sirupsen/logrus"
27 "os/exec"
28)
29
30var (
31 nniLogger = log.WithFields(log.Fields{"module": "NNI"})
32 nniVeth = "nni"
33 upstreamVeth = "upstream"
34 dhcpServerIp = "182.21.0.128"
35)
36
Matteo Scandolo86e8ce62019-10-11 12:03:10 -070037type NniPort struct {
38 // BBSIM Internals
39 ID uint32
40
41 // PON Attributes
42 OperState *fsm.FSM
43 Type string
44}
45
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -070046func CreateNNI(olt *OltDevice) (NniPort, error) {
47 nniPort := NniPort{
48 ID: uint32(0),
49 OperState: getOperStateFSM(func(e *fsm.Event) {
50 oltLogger.Debugf("Changing NNI OperState from %s to %s", e.Src, e.Dst)
51 }),
52 Type: "nni",
53 }
54 createNNIPair(olt)
55 return nniPort, nil
56}
57
58// sendNniPacket will send a packet out of the NNI interface.
59// We will send upstream only DHCP packets and drop anything else
60func sendNniPacket(packet gopacket.Packet) error {
61 if isDhcp := packetHandlers.IsDhcpPacket(packet); !isDhcp {
62 nniLogger.WithFields(log.Fields{
63 "packet": packet,
64 }).Trace("Dropping NNI packet as it's not DHCP")
65 }
66
67 packet, err := packetHandlers.PopDoubleTag(packet)
68 if err != nil {
69 nniLogger.WithFields(log.Fields{
70 "packet": packet,
71 }).Errorf("Can't remove double tags from packet: %v", err)
72 return err
73 }
74
75 handle, err := getVethHandler(nniVeth)
76 if err != nil {
77 return err
78 }
79
80 err = handle.WritePacketData(packet.Data())
81 if err != nil {
82 nniLogger.WithFields(log.Fields{
83 "packet": packet,
84 }).Errorf("Failed to send packet out of the NNI: %s", err)
85 return err
86 }
87
88 nniLogger.Infof("Sent packet out of NNI")
89 return nil
90}
91
92// createNNIBridge will create a veth bridge to fake the connection between the NNI port
93// and something upstream, in this case a DHCP server.
94// It is also responsible to start the DHCP server itself
95func createNNIPair(olt *OltDevice) error {
96
97 if err := exec.Command("ip", "link", "add", nniVeth, "type", "veth", "peer", "name", upstreamVeth).Run(); err != nil {
98 nniLogger.Errorf("Couldn't create veth pair between %s and %s", nniVeth, upstreamVeth)
99 return err
100 }
101
102 if err := setVethUp(nniVeth); err != nil {
103 return err
104 }
105
106 if err := setVethUp(upstreamVeth); err != nil {
107 return err
108 }
109
110 if err := startDHCPServer(); err != nil {
111 return err
112 }
113
114 ch, err := listenOnVeth(nniVeth)
115 if err != nil {
116 return err
117 }
118 olt.nniPktInChannel = ch
119 return nil
120}
121
122// setVethUp is responsible to activate a virtual interface
123func setVethUp(vethName string) error {
124 if err := exec.Command("ip", "link", "set", vethName, "up").Run(); err != nil {
125 nniLogger.Errorf("Couldn't change interface %s state to up: %v", vethName, err)
126 return err
127 }
128 return nil
129}
130
131func startDHCPServer() error {
132 if err := exec.Command("ip", "addr", "add", dhcpServerIp, "dev", upstreamVeth).Run(); err != nil {
133 nniLogger.Errorf("Couldn't assing ip %s to interface %s: %v", dhcpServerIp, upstreamVeth, err)
134 return err
135 }
136
137 if err := setVethUp(upstreamVeth); err != nil {
138 return err
139 }
140
141 dhcp := "/usr/local/bin/dhcpd"
142 conf := "/etc/dhcp/dhcpd.conf" // copied in the container from configs/dhcpd.conf
143 logfile := "/tmp/dhcplog"
144 var stderr bytes.Buffer
145 cmd := exec.Command(dhcp, "-cf", conf, upstreamVeth, "-tf", logfile)
146 cmd.Stderr = &stderr
147 err := cmd.Run()
148 if err != nil {
149 nniLogger.Errorf("Fail to start DHCP Server: %s, %s", err, stderr.String())
150 return err
151 }
152 nniLogger.Info("Successfully activated DHCP Server")
153 return nil
154}
155
156func getVethHandler(vethName string) (*pcap.Handle, error) {
157 var (
158 device = vethName
159 snapshotLen int32 = 1518
160 promiscuous = false
161 timeout = pcap.BlockForever
162 )
163 handle, err := pcap.OpenLive(device, snapshotLen, promiscuous, timeout)
164 if err != nil {
165 nniLogger.Errorf("Can't retrieve handler for interface %s", vethName)
166 return nil, err
167 }
168 return handle, nil
169}
170
171func listenOnVeth(vethName string) (chan *types.PacketMsg, error) {
172
173 handle, err := getVethHandler(vethName)
174 if err != nil {
175 return nil, err
176 }
177
178 channel := make(chan *types.PacketMsg, 32)
179
180 go func() {
181 packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
182 for packet := range packetSource.Packets() {
183
184 if !packetHandlers.IsIncomingPacket(packet) {
185 nniLogger.Tracef("Ignoring packet as it's going out")
186 continue
187 }
188
189 nniLogger.WithFields(log.Fields{
190 "packet": packet.Dump(),
191 }).Tracef("Received packet on NNI Port")
192 pkt := types.PacketMsg{
193 Pkt: packet,
194 }
195 channel <- &pkt
196 }
197 }()
198
199 return channel, nil
200}