<commit_before>package router

import (
	"strconv"
	"bytes"
	"net"
	"fmt"
	"errors"

	//"github.com/netgroup-polito/iovisor-ovn/config"
	"github.com/netgroup-polito/iovisor-ovn/hoverctl"
	l "github.com/op/go-logging"
)

var log = l.MustGetLogger("iomodules-switch")

type RouterModule struct {
	ModuleId	string
	PortsCount	int                               //number of allocated ports
	PortsArray	[10]bool // This is used to keep track of available index in
						 // the router_port map of the switch
	RoutingTable [10]RoutingTableEntry
	Interfaces map[string]*RouterModuleInterface

	deployed	bool
	dataplane	*hoverctl.Dataplane	// used to send commands to hover
}

type RouterModuleInterface struct {
	IfaceIdRedirectHover  int    //Iface id inside hover (relative to the m:1234 the interface is attached to ...) and provided my the extended hover /links/ API
	IfaceFd               int    //Interface Fd inside External_Ids (42, etc...)
	LinkIdHover           string //iomodules Link Id
	IfaceName             string
	IP                    string
	Netmask               string
	MAC                   string
	portIndex             int
}

type RoutingTableEntry struct {
	network string
	netmask    string
	port    int
}

func Create(dp *hoverctl.Dataplane) *RouterModule {

	if dp == nil {
		log.Errorf("Daplane is not valid\n")
		return nil
	}

	r := new(RouterModule)
	r.Interfaces = make(map[string]*RouterModuleInterface)
	//r.PortsArray = make(map[int]bool)
	r.dataplane = dp
	r.deployed = false
	return r
}

func (r *RouterModule) GetModuleId() string {
	return r.ModuleId
}

func (r *RouterModule) Deploy() (err error) {

	if r.deployed {
		return nil
	}

	routerError, routerHover := hoverctl.ModulePOST(r.dataplane, "bpf",
									"Router", RouterCode)
	if routerError != nil {
		log.Errorf("Error in POST Router IOModule: %s\n", routerError)
		return routerError
	}

	log.Noticef("POST Router IOModule %s\n", routerHover.Id)
	r.ModuleId = routerHover.Id
	r.deployed = true

	return nil
}

func (r *RouterModule) Destroy() (err error) {

	if !r.deployed {
		return nil
	}

	// TODO:
	// All interfaces must be detached before destroying the module.
	// Should it be done automatically here, or should be the application responsible for that?

	moduleDeleteError, _ := hoverctl.ModuleDELETE(r.dataplane, r.ModuleId)
	if moduleDeleteError != nil {
		log.Errorf("Error in destrying Router IOModule: %s\n", moduleDeleteError)
		return moduleDeleteError
	}

	r.ModuleId = ""
	r.deployed = false

	return nil
}

func (r *RouterModule) AttachExternalInterface(ifaceName string) (err error) {

	if !r.deployed {
		errString := "Trying to attach port in undeployed router"
		log.Errorf(errString)
		return errors.New(errString)
	}

	portIndex := -1

	for i := 0; i < 10; i++ {
		if !r.PortsArray[i] {
			portIndex = i
			break
		}
	}

	if portIndex == -1 {
		errString := "There are not free ports in the router\n"
		log.Errorf(errString)
		return errors.New(errString)
	}

	linkError, linkHover := hoverctl.LinkPOST(r.dataplane, "i:" + ifaceName, r.ModuleId)
	if linkError != nil {
		log.Errorf("Error in POSTing the Link: %s\n", linkError)
		return linkError
	}

	_, external_interfaces := hoverctl.ExternalInterfacesListGET(r.dataplane)

	r.PortsArray[portIndex] = true
	r.PortsCount++

	// Saving IfaceIdRedirectHover for this port. The number will be used by security policies
	ifacenumber := -1
	if linkHover.From[0:2] == "m:" {
		ifacenumber = linkHover.FromId
	}
	if linkHover.To[0:2] == "m:" {
		ifacenumber = linkHover.ToId
	}
	if ifacenumber == -1 {
		log.Warningf("IfaceIdRedirectHover == -1 something wrong happend...\n")
	}

	iface := new(RouterModuleInterface)

	iface.IfaceFd, _ = strconv.Atoi(external_interfaces[ifaceName].Id)
	iface.IfaceIdRedirectHover = ifacenumber
	iface.LinkIdHover = linkHover.Id
	iface.IfaceName = ifaceName
	iface.portIndex = portIndex

	r.Interfaces[ifaceName] = iface

	return nil
}

func (r *RouterModule) DetachExternalInterface(ifaceName string) (err error) {

	if !r.deployed {
		errString := "Trying to detach port in undeployed switch"
		log.Errorf(errString)
		return errors.New(errString)
	}

	iface, ok := r.Interfaces[ifaceName]

	if !ok {
		errString := fmt.Sprintf("Iface '%s' is not present in router '%s'\n",
			ifaceName, r.ModuleId)
		log.Warningf(errString)
		return errors.New(errString)
	}

	linkDeleteError, _ := hoverctl.LinkDELETE(r.dataplane, iface.LinkIdHover)

	if linkDeleteError != nil {
		//log.Debug("REMOVE Interface %s %s (1/1) LINK REMOVED\n", currentInterface.Name, currentInterface.IfaceIdExternalIds)
		log.Warningf("Problem removing iface '%s' from router '%s'\n",
			ifaceName, r.ModuleId)
		return linkDeleteError
	}

	// indicate that the port index is available
	r.PortsArray[iface.portIndex] = false

	// remove port from list of ports
	portIndexString := strconv.Itoa(iface.portIndex)
	hoverctl.TableEntryDELETE(r.dataplane, r.ModuleId, "router_port", portIndexString)

	// TODO: remove static route entry
	delete(r.Interfaces, ifaceName)

	return nil
}

func (r *RouterModule) AttachToIoModule(ifaceId int, ifaceName string) (err error) {
	if !r.deployed {
		errString := "Trying to attach port in undeployed router"
		log.Errorf(errString)
		return errors.New(errString)
	}

	portIndex := -1

	for i := 0; i < 10; i++ {
		if !r.PortsArray[i] {
			portIndex = i
			break
		}
	}

	if portIndex == -1 {
		errString := "There are not free ports in the router"
		log.Errorf(errString)
		return errors.New(errString)
	}

	iface := new(RouterModuleInterface)

	iface.IfaceFd = -1
	iface.IfaceIdRedirectHover = ifaceId
	iface.LinkIdHover = ""
	iface.IfaceName = ifaceName
	iface.portIndex = portIndex

	r.Interfaces[ifaceName] = iface

	return nil
}

func (r *RouterModule) DetachFromIoModule(ifaceName string) (err error) {
	return errors.New("Not implemented")
}

// After a interface has been added, it is necessary to configure it before
// it can be used to route packets
func (r *RouterModule) ConfigureInterface(ifaceName string, ip string, netmask string, mac string) (err error) {
	if !r.deployed {
		errString := "Trying to configure an interface in undeployed router"
		log.Errorf(errString)
		return errors.New(errString)
	}

	iface, ok := r.Interfaces[ifaceName]

	if !ok {
		errString := fmt.Sprintf("Iface '%s' is not present in router '%s'\n",
			ifaceName, r.ModuleId)
		log.Warningf(errString)
		return errors.New(errString)
	}

	// TODO: check ip, netmask and mac

	iface.IP = ip
	iface.Netmask = netmask
	iface.MAC = mac

	// configure port entry
	portIndexString := strconv.Itoa(iface.portIndex)
	ipString := ipToHexadecimalString(ip)
	netmaskString := ipToHexadecimalString(netmask)
	macString := macToHexadecimalString(mac)

	toSend := ipString + " " + netmaskString + " " + macString

	hoverctl.TableEntryPOST(r.dataplane, r.ModuleId, "router_port",
		portIndexString, toSend)

	ip_ := net.ParseIP(ip)
	netmask_ := ParseIPv4Mask(netmask)
	network_ := ip_.Mask(netmask_)

	// add route for that port
	if !r.AddRoutingTableEntry(network_.String(), netmask, iface.IfaceIdRedirectHover) {
		errString := fmt.Sprintf("Error adding static route for port '%s' in router '%s'\n",
			ifaceName, r.ModuleId)
		log.Warningf(errString)
		return errors.New(errString)
	}

	return nil
}<commit_msg>router: fix bad logic when configuring ports<commit_after>package router

import (
	"strconv"
	"bytes"
	"net"
	"fmt"
	"errors"

	//"github.com/netgroup-polito/iovisor-ovn/config"
	"github.com/netgroup-polito/iovisor-ovn/hoverctl"
	l "github.com/op/go-logging"
)

var log = l.MustGetLogger("iomodules-switch")

type RouterModule struct {
	ModuleId	string
	PortsCount	int                               //number of allocated ports
	RoutingTable [10]RoutingTableEntry
	Interfaces map[string]*RouterModuleInterface

	deployed	bool
	dataplane	*hoverctl.Dataplane	// used to send commands to hover
}

type RouterModuleInterface struct {
	IfaceIdRedirectHover  int    //Iface id inside hover (relative to the m:1234 the interface is attached to ...) and provided my the extended hover /links/ API
	IfaceFd               int    //Interface Fd inside External_Ids (42, etc...)
	LinkIdHover           string //iomodules Link Id
	IfaceName             string
	IP                    string
	Netmask               string
	MAC                   string
}

type RoutingTableEntry struct {
	network string
	netmask    string
	port    int
}

func Create(dp *hoverctl.Dataplane) *RouterModule {

	if dp == nil {
		log.Errorf("Daplane is not valid\n")
		return nil
	}

	r := new(RouterModule)
	r.Interfaces = make(map[string]*RouterModuleInterface)
	r.dataplane = dp
	r.deployed = false
	return r
}

func (r *RouterModule) GetModuleId() string {
	return r.ModuleId
}

func (r *RouterModule) Deploy() (err error) {

	if r.deployed {
		return nil
	}

	routerError, routerHover := hoverctl.ModulePOST(r.dataplane, "bpf",
									"Router", RouterCode)
	if routerError != nil {
		log.Errorf("Error in POST Router IOModule: %s\n", routerError)
		return routerError
	}

	log.Noticef("POST Router IOModule %s\n", routerHover.Id)
	r.ModuleId = routerHover.Id
	r.deployed = true

	return nil
}

func (r *RouterModule) Destroy() (err error) {

	if !r.deployed {
		return nil
	}

	// TODO:
	// All interfaces must be detached before destroying the module.
	// Should it be done automatically here, or should be the application responsible for that?

	moduleDeleteError, _ := hoverctl.ModuleDELETE(r.dataplane, r.ModuleId)
	if moduleDeleteError != nil {
		log.Errorf("Error in destrying Router IOModule: %s\n", moduleDeleteError)
		return moduleDeleteError
	}

	r.ModuleId = ""
	r.deployed = false

	return nil
}

func (r *RouterModule) AttachExternalInterface(ifaceName string) (err error) {

	if !r.deployed {
		errString := "Trying to attach port in undeployed router"
		log.Errorf(errString)
		return errors.New(errString)
	}

	if r.PortsCount == 10 {
		errString := "There are not free ports in the router\n"
		log.Errorf(errString)
		return errors.New(errString)
	}

	linkError, linkHover := hoverctl.LinkPOST(r.dataplane, "i:" + ifaceName, r.ModuleId)
	if linkError != nil {
		log.Errorf("Error in POSTing the Link: %s\n", linkError)
		return linkError
	}

	_, external_interfaces := hoverctl.ExternalInterfacesListGET(r.dataplane)

	r.PortsCount++

	// Saving IfaceIdRedirectHover for this port. The number will be used by security policies
	ifacenumber := -1
	if linkHover.From[0:2] == "m:" {
		ifacenumber = linkHover.FromId
	}
	if linkHover.To[0:2] == "m:" {
		ifacenumber = linkHover.ToId
	}
	if ifacenumber == -1 {
		log.Warningf("IfaceIdRedirectHover == -1 something wrong happend...\n")
	}

	iface := new(RouterModuleInterface)

	iface.IfaceFd, _ = strconv.Atoi(external_interfaces[ifaceName].Id)
	iface.IfaceIdRedirectHover = ifacenumber
	iface.LinkIdHover = linkHover.Id
	iface.IfaceName = ifaceName

	r.Interfaces[ifaceName] = iface

	return nil
}

func (r *RouterModule) DetachExternalInterface(ifaceName string) (err error) {

	if !r.deployed {
		errString := "Trying to detach port in undeployed switch"
		log.Errorf(errString)
		return errors.New(errString)
	}

	iface, ok := r.Interfaces[ifaceName]

	if !ok {
		errString := fmt.Sprintf("Iface '%s' is not present in router '%s'\n",
			ifaceName, r.ModuleId)
		log.Warningf(errString)
		return errors.New(errString)
	}

	linkDeleteError, _ := hoverctl.LinkDELETE(r.dataplane, iface.LinkIdHover)

	if linkDeleteError != nil {
		//log.Debug("REMOVE Interface %s %s (1/1) LINK REMOVED\n", currentInterface.Name, currentInterface.IfaceIdExternalIds)
		log.Warningf("Problem removing iface '%s' from router '%s'\n",
			ifaceName, r.ModuleId)
		return linkDeleteError
	}

	// remove port from list of ports
	ifaceIdString := strconv.Itoa(iface.IfaceIdRedirectHover)
	hoverctl.TableEntryDELETE(r.dataplane, r.ModuleId, "router_port", ifaceIdString)

	// TODO: remove static route entry
	delete(r.Interfaces, ifaceName)

	return nil
}

func (r *RouterModule) AttachToIoModule(ifaceId int, ifaceName string) (err error) {
	if !r.deployed {
		errString := "Trying to attach port in undeployed router"
		log.Errorf(errString)
		return errors.New(errString)
	}

	if r.PortsCount == 10 {
		errString := "There are not free ports in the router"
		log.Errorf(errString)
		return errors.New(errString)
	}

	iface := new(RouterModuleInterface)

	iface.IfaceFd = -1
	iface.IfaceIdRedirectHover = ifaceId
	iface.LinkIdHover = ""
	iface.IfaceName = ifaceName

	r.Interfaces[ifaceName] = iface

	return nil
}

func (r *RouterModule) DetachFromIoModule(ifaceName string) (err error) {
	return errors.New("Not implemented")
}

// After a interface has been added, it is necessary to configure it before
// it can be used to route packets
func (r *RouterModule) ConfigureInterface(ifaceName string, ip string, netmask string, mac string) (err error) {
	if !r.deployed {
		errString := "Trying to configure an interface in undeployed router"
		log.Errorf(errString)
		return errors.New(errString)
	}

	iface, ok := r.Interfaces[ifaceName]

	if !ok {
		errString := fmt.Sprintf("Iface '%s' is not present in router '%s'\n",
			ifaceName, r.ModuleId)
		log.Warningf(errString)
		return errors.New(errString)
	}

	// TODO: check ip, netmask and mac

	iface.IP = ip
	iface.Netmask = netmask
	iface.MAC = mac

	// configure port entry
	ifaceIdString := strconv.Itoa(iface.IfaceIdRedirectHover)
	ipString := ipToHexadecimalString(ip)
	netmaskString := ipToHexadecimalString(netmask)
	macString := macToHexadecimalString(mac)

	toSend := ipString + " " + netmaskString + " " + macString

	hoverctl.TableEntryPOST(r.dataplane, r.ModuleId, "router_port",
		ifaceIdString, toSend)

	ip_ := net.ParseIP(ip)
	netmask_ := ParseIPv4Mask(netmask)
	network_ := ip_.Mask(netmask_)

	// add route for that port
	if !r.AddRoutingTableEntry(network_.String(), netmask, iface.IfaceIdRedirectHover) {
		errString := fmt.Sprintf("Error adding static route for port '%s' in router '%s'\n",
			ifaceName, r.ModuleId)
		log.Warningf(errString)
		return errors.New(errString)
	}

	return nil
}
{'fraction_non_alphanumeric': 0.10970584055705557, 'fraction_numerical': 0.0038368622992752594, 'mean_word_length': 3.8400962861072903, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5499118', 'n_tokens_mistral': 5086, 'n_tokens_neox': 4864, 'n_words': 1267}