blob: d1564bad8d6272ab846c171bb4360d6df96739b6 [file] [log] [blame]
/*
* Copyright 2017-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 (
"encoding/json"
"fmt"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/opencord/voltha/ponsim/v2/common"
"github.com/opencord/voltha/protos/go/voltha"
"github.com/sirupsen/logrus"
"math/rand"
"net"
"time"
)
// TODO: user-defined values? min/max intervals, vlan?
const (
minInterval = 20
maxInterval = 60
vlandId = 4000
localhost = "127.0.0.1"
ttl = 64
ipVersion = 4
)
type Alarm struct {
Severity int `json:"severity"`
Type int `json:"type"`
Category int `json:"category"`
State int `json:"state"`
TimeStamp int `json:"ts"`
Description string `json:"description"`
}
/*
PonSimAlarm is the structure responsible for the handling of alarms
*/
type PonSimAlarm struct {
forwardFunction func(int, gopacket.Packet)
dstInterface string
dstEndpoint string
}
/*
NewPonSimAlarm instantiates a new alarm handling structure
*/
func NewPonSimAlarm(dstInterface string, dstEndpoint string, function func(int, gopacket.Packet)) *PonSimAlarm {
psa := &PonSimAlarm{dstInterface: dstInterface, dstEndpoint: dstEndpoint, forwardFunction: function}
return psa
}
/*
prepareAlarm constructs an alarm object with random field values.
*/
func (a *PonSimAlarm) prepareAlarm() *Alarm {
alarm_severity := rand.Intn(len(voltha.AlarmEventSeverity_AlarmEventSeverity_value))
alarm_type := rand.Intn(len(voltha.AlarmEventType_AlarmEventType_value))
alarm_category := rand.Intn(len(voltha.AlarmEventCategory_AlarmEventCategory_value))
alarm_state := int(voltha.AlarmEventState_RAISED)
alarm_ts := time.Now().UTC().Second()
alarm_description := fmt.Sprintf("%s.%s alarm",
voltha.AlarmEventType_AlarmEventType_name[int32(alarm_type)],
voltha.AlarmEventCategory_AlarmEventCategory_name[int32(alarm_category)],
)
alarm := &Alarm{
Severity: alarm_severity,
Type: alarm_type,
Category: alarm_category,
State: alarm_state,
TimeStamp: alarm_ts,
Description: alarm_description,
}
return alarm
}
/*
sendAlarm constructs and forwards the alarm to the network
*/
func (a *PonSimAlarm) sendAlarm(alarm *Alarm) {
// Ethernet layer is configured as a broadcast packet
ethLayer := &layers.Ethernet{
SrcMAC: common.GetMacAddress(a.dstInterface),
DstMAC: layers.EthernetBroadcast,
EthernetType: layers.EthernetTypeDot1Q,
}
// Need to encapsulate in VLAN so that voltha captures the packet
dot1qLayer := &layers.Dot1Q{
Type: layers.EthernetTypeIPv4,
VLANIdentifier: vlandId,
}
common.Logger().WithFields(logrus.Fields{
"Alarm": a,
"srcIp": common.GetInterfaceIP(a.dstInterface),
"dstIp": common.GetHostIP(a.dstEndpoint),
}).Info("SRC/DST IP addresses")
// IP layer needs the following attributes at a minimum in order to have
// a properly formed packet
ipLayer := &layers.IPv4{
SrcIP: net.ParseIP(common.GetInterfaceIP(a.dstInterface)),
DstIP: net.ParseIP(common.GetHostIP(a.dstEndpoint)),
//SrcIP: net.ParseIP(localhost),
//DstIP: net.ParseIP(localhost),
Version: ipVersion,
TTL: ttl,
Protocol: layers.IPProtocolTCP,
}
// TCP layer does not require anything special
// except than providing the IP layer so that the checksum can be
// properly calculated
tcpLayer := &layers.TCP{}
tcpLayer.SetNetworkLayerForChecksum(ipLayer)
// Convert the alarm to bytes to include it as the packet payload
rawData, _ := json.Marshal(alarm)
// Construct the packet
buffer := gopacket.NewSerializeBuffer()
options := gopacket.SerializeOptions{
FixLengths: true,
ComputeChecksums: true,
}
gopacket.SerializeLayers(buffer, options,
ethLayer,
dot1qLayer,
ipLayer,
tcpLayer,
gopacket.Payload(rawData),
)
frame := gopacket.NewPacket(
buffer.Bytes(),
layers.LayerTypeEthernet,
gopacket.Default,
)
// Forward the packetized alarm to the network
a.forwardFunction(0, frame)
common.Logger().WithFields(logrus.Fields{
"Alarm": alarm,
"Frame": frame.Dump(),
}).Debug("Sent alarm")
}
/*
raiseAlarm submits an alarm object with a RAISED state
*/
func (a *PonSimAlarm) raiseAlarm(alarm *Alarm) {
alarm.State = int(voltha.AlarmEventState_RAISED)
a.sendAlarm(alarm)
}
/*
clearAlarm submits an alarm object with a CLEARED state
*/
func (a *PonSimAlarm) clearAlarm(alarm *Alarm) {
alarm.State = int(voltha.AlarmEventState_CLEARED)
a.sendAlarm(alarm)
}
/*
GenerateAlarm simulates RAISE and CLEAR alarm events with a random delay in between each state.
*/
func (a *PonSimAlarm) GenerateAlarm() {
alarm := a.prepareAlarm()
a.raiseAlarm(alarm)
time.Sleep(time.Duration(rand.Intn(maxInterval-minInterval)+minInterval) * time.Second)
a.clearAlarm(alarm)
}