<commit_before>// Copyright 2017 ETH Zurich
//
// 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 models

import (
	"time"

	"fmt"

	"errors"

	"github.com/astaxie/beego/orm"
	"github.com/netsec-ethz/scion-coord/config"
	"github.com/netsec-ethz/scion-coord/utility"
	"github.com/netsec-ethz/scion/go/lib/addr"
)

// TODO(mlegner): Some of the functions here may not be optimally efficient

type AttachmentPoint struct {
	ID          uint64 `orm:"column(id);auto;pk"`
	VPNIP       string
	StartVPNIP  string
	EndVPNIP    string
	AS          *SCIONLabAS   `orm:"reverse(one)"`
	Connections []*Connection `orm:"reverse(many);index"` // List of Connections
}

// TODO(philippmao, mlegner): Link SCIONLabAS to user model?
// TODO(mlegner): Maybe it would make more sense to replace the user by an account here
type SCIONLabAS struct {
	ID          uint64 `orm:"column(id);auto;pk"`
	UserEmail   string // Owner of the AS
	PublicIP    string // IP address of the SCIONLabAS; can be empty in case of VPN-based setups
	StartPort   uint16 // First port used for border routers
	ISD         int    `orm:"default(0)"` // 0 means no ISD is joined
	ASID        int
	Core        bool             `orm:"default(false)"` // Is this SCIONLabAS a core AS
	Label       string           // Optional label for this AS (can be chosen by the user)
	Status      uint8            `orm:"default(0)"` // Status of the AS: ACTIVE, CREATE, ...
	Type        uint8            `orm:"default(0)"` // Type of the AS: BOX, VM, DEDICATED, ...
	AP          *AttachmentPoint `orm:"null;rel(one);on_delete(set_null)"`
	Credits     int64
	Created     time.Time
	Updated     time.Time
	Connections []*Connection `orm:"reverse(many)"` // List of Connections
}

type Connection struct {
	ID            uint64           `orm:"column(id);auto;pk"`
	JoinAS        *SCIONLabAS      `orm:"rel(fk)"` // AS which initiated the connection
	RespondAP     *AttachmentPoint `orm:"rel(fk)"` // AS which accepted the connection
	JoinIP        string           // IP address used for the joining AS
	RespondIP     string           // IP address used for the responding AS
	JoinBRID      uint16           // ID of the joining border router, Port = StartPort + BRID
	RespondBRID   uint16           // ID of the responding AS's border router
	Linktype      uint8            // role of the responding AS
	IsVPN         bool
	JoinStatus    uint8
	RespondStatus uint8
	Created       time.Time
	Updated       time.Time
}

// Contains all info needed to populate the topology file
type ConnectionInfo struct {
	ID             uint64 // Used to find the BorderRouter
	NeighborISD    int
	NeighborAS     int
	NeighborIP     string
	NeighborUser   string
	NeighborStatus uint8
	LocalIP        string
	BindIP         string
	BRID           uint16
	NeighborPort   uint16 // port of the neighbor's border router
	LocalPort      uint16 // port of the local border router
	Linktype       uint8  //"PARENT","CHILD"
	IsVPN          bool
	Status         uint8
}

func (as *SCIONLabAS) IA() string {
	if as.ISD < 1 {
		return fmt.Sprintf("%v", as.ASID)
	}
	return utility.IAString(as.ISD, as.ASID)
}

func (as *SCIONLabAS) String() string {
	res := as.IA()
	if as.Label != "" {
		res = fmt.Sprintf("%v (%v)", as.IA(), as.Label)
	}
	return res
}

// This function determines the IP address that are used for different SCION servers (CS, BS, PS)
func (as *SCIONLabAS) ServerIP() string {
	switch as.Type {
	case INFRASTRUCTURE:
		return as.PublicIP
	case VM:
		return config.VM_LOCAL_IP
	default:
		return "127.0.0.1"
	}
}

// This function determines the BindIP address used for the border router of a given connection
// TODO(mlegner): This should be replaced by an iptables rule and simply the ServerIP here
func (as *SCIONLabAS) BindIP(isVPN bool, connectionIP string) string {
	if isVPN {
		return connectionIP
	}
	return as.ServerIP()
}

func (cn *Connection) JoinBindIP() string {
	as := cn.JoinAS
	return as.BindIP(cn.IsVPN, cn.JoinIP)
}

func (cn *Connection) RespondBindIP() string {
	ap := cn.RespondAP
	o.LoadRelated(ap, "AS")
	if ap.AS == nil {
		return ""
	}
	return ap.AS.BindIP(cn.IsVPN, cn.RespondIP)
}

func (ap *AttachmentPoint) Insert() error {
	_, err := o.Insert(ap)
	return err
}

func (ap *AttachmentPoint) Update() error {
	_, err := o.Update(ap)
	return err
}

func (as *SCIONLabAS) Insert() error {
	as.Created = time.Now().UTC()
	as.Updated = time.Now().UTC()
	_, err := o.Insert(as)
	return err
}

func (as *SCIONLabAS) Update() error {
	as.Updated = time.Now().UTC()
	_, err := o.Update(as)
	return err
}

func (cn *Connection) Insert() error {
	cn.Created = time.Now().UTC()
	cn.Updated = time.Now().UTC()
	_, err := o.Insert(cn)
	return err
}

func (cn *Connection) Update() error {
	cn.Updated = time.Now().UTC()
	_, err := o.Update(cn)
	return err
}

func (ap *AttachmentPoint) getConnections() ([]*Connection, error) {
	_, err := o.LoadRelated(ap, "Connections")
	return ap.Connections, err
}

func (as *SCIONLabAS) GetPortNumberFromBRID(brID uint16) uint16 {
	return as.StartPort + brID - 1
}

func (as *SCIONLabAS) GetFreeBRID() (uint16, error) {
	cns, err := as.GetConnectionInfo()
	if err != nil {
		return 0, fmt.Errorf("Error finding connections of AS %v: %v", as.IA(), err)
	}
	length := len(cns)
	brIDs := make([]int, length)
	for i, cn := range cns {
		brIDs[i] = int(cn.BRID)
	}
	minBRID := 1
	if as.Type != INFRASTRUCTURE {
		minBRID += config.RESERVED_BRS_INFRASTRUCTURE
	}
	id, err := utility.GetFreeID(brIDs, minBRID, config.MAX_BR_ID)
	return uint16(id), err
}

// TODO(mlegner): Avoid signed/unsigned casting; could be problematic if huge IP ranges are used
func (as *SCIONLabAS) GetFreeVPNIP() (string, error) {
	cns, err := as.getRespondConnections()
	if err != nil {
		return "", fmt.Errorf("Error finding connections of AP %v: %v", as.IA(), err)
	}
	vpnIPs := []int{}
	for _, cn := range cns {
		if cn.IsVPN {
			vpnIPs = append(vpnIPs, int(utility.IPToInt(cn.JoinIP)))<commit_msg>Fixed two small bugs introduced in #136 (#153)<commit_after>// Copyright 2017 ETH Zurich
//
// 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 models

import (
	"time"

	"fmt"

	"errors"

	"github.com/astaxie/beego/orm"
	"github.com/netsec-ethz/scion-coord/config"
	"github.com/netsec-ethz/scion-coord/utility"
	"github.com/netsec-ethz/scion/go/lib/addr"
)

// TODO(mlegner): Some of the functions here may not be optimally efficient

type AttachmentPoint struct {
	ID          uint64 `orm:"column(id);auto;pk"`
	VPNIP       string
	StartVPNIP  string
	EndVPNIP    string
	AS          *SCIONLabAS   `orm:"reverse(one)"`
	Connections []*Connection `orm:"reverse(many);index"` // List of Connections
}

// TODO(philippmao, mlegner): Link SCIONLabAS to user model?
// TODO(mlegner): Maybe it would make more sense to replace the user by an account here
type SCIONLabAS struct {
	ID          uint64 `orm:"column(id);auto;pk"`
	UserEmail   string // Owner of the AS
	PublicIP    string // IP address of the SCIONLabAS; can be empty in case of VPN-based setups
	StartPort   uint16 // First port used for border routers
	ISD         int    `orm:"default(0)"` // 0 means no ISD is joined
	ASID        int
	Core        bool             `orm:"default(false)"` // Is this SCIONLabAS a core AS
	Label       string           // Optional label for this AS (can be chosen by the user)
	Status      uint8            `orm:"default(0)"` // Status of the AS: ACTIVE, CREATE, ...
	Type        uint8            `orm:"default(0)"` // Type of the AS: BOX, VM, DEDICATED, ...
	AP          *AttachmentPoint `orm:"null;rel(one);on_delete(set_null)"`
	Credits     int64
	Created     time.Time
	Updated     time.Time
	Connections []*Connection `orm:"reverse(many)"` // List of Connections
}

type Connection struct {
	ID            uint64           `orm:"column(id);auto;pk"`
	JoinAS        *SCIONLabAS      `orm:"rel(fk)"` // AS which initiated the connection
	RespondAP     *AttachmentPoint `orm:"rel(fk)"` // AS which accepted the connection
	JoinIP        string           // IP address used for the joining AS
	RespondIP     string           // IP address used for the responding AS
	JoinBRID      uint16           // ID of the joining border router, Port = StartPort + BRID
	RespondBRID   uint16           // ID of the responding AS's border router
	Linktype      uint8            // role of the responding AS
	IsVPN         bool
	JoinStatus    uint8
	RespondStatus uint8
	Created       time.Time
	Updated       time.Time
}

// Contains all info needed to populate the topology file
type ConnectionInfo struct {
	ID             uint64 // Used to find the BorderRouter
	NeighborISD    int
	NeighborAS     int
	NeighborIP     string
	NeighborUser   string
	NeighborStatus uint8
	LocalIP        string
	BindIP         string
	BRID           uint16
	NeighborPort   uint16 // port of the neighbor's border router
	LocalPort      uint16 // port of the local border router
	Linktype       uint8  //"PARENT","CHILD"
	IsVPN          bool
	Status         uint8
}

func (as *SCIONLabAS) IA() string {
	if as.ISD < 1 {
		return fmt.Sprintf("%v", as.ASID)
	}
	return utility.IAString(as.ISD, as.ASID)
}

func (as *SCIONLabAS) String() string {
	res := as.IA()
	if as.Label != "" {
		res = fmt.Sprintf("%v (%v)", as.IA(), as.Label)
	}
	return res
}

// This function determines the IP address that are used for different SCION servers (CS, BS, PS)
func (as *SCIONLabAS) ServerIP() string {
	switch as.Type {
	case INFRASTRUCTURE, DEDICATED:
		return as.PublicIP
	case VM:
		return config.VM_LOCAL_IP
	default:
		return "127.0.0.1"
	}
}

// This function determines the BindIP address used for the border router of a given connection
// TODO(mlegner): This should be replaced by an iptables rule and simply the ServerIP here
func (as *SCIONLabAS) BindIP(isVPN bool, connectionIP string) string {
	if isVPN {
		return connectionIP
	}
	return as.ServerIP()
}

func (cn *Connection) JoinBindIP() string {
	as := cn.JoinAS
	return as.BindIP(cn.IsVPN, cn.JoinIP)
}

func (cn *Connection) RespondBindIP() string {
	ap := cn.RespondAP
	o.LoadRelated(ap, "AS")
	if ap.AS == nil {
		return ""
	}
	return ap.AS.BindIP(cn.IsVPN, cn.RespondIP)
}

func (ap *AttachmentPoint) Insert() error {
	_, err := o.Insert(ap)
	return err
}

func (ap *AttachmentPoint) Update() error {
	_, err := o.Update(ap)
	return err
}

func (as *SCIONLabAS) Insert() error {
	as.Created = time.Now().UTC()
	as.Updated = time.Now().UTC()
	_, err := o.Insert(as)
	return err
}

func (as *SCIONLabAS) Update() error {
	as.Updated = time.Now().UTC()
	_, err := o.Update(as)
	return err
}

func (cn *Connection) Insert() error {
	cn.Created = time.Now().UTC()
	cn.Updated = time.Now().UTC()
	_, err := o.Insert(cn)
	return err
}

func (cn *Connection) Update() error {
	cn.Updated = time.Now().UTC()
	_, err := o.Update(cn)
	return err
}

func (ap *AttachmentPoint) getConnections() ([]*Connection, error) {
	_, err := o.LoadRelated(ap, "Connections")
	return ap.Connections, err
}

func (as *SCIONLabAS) GetPortNumberFromBRID(brID uint16) uint16 {
	return as.StartPort + brID - 1
}

func (as *SCIONLabAS) GetFreeBRID() (uint16, error) {
	cns, err := as.GetConnectionInfo()
	if err != nil {
		return 0, fmt.Errorf("Error finding connections of AS %v: %v", as.IA(), err)
	}
	length := len(cns)
	brIDs := make([]int, length)
	for i, cn := range cns {
		brIDs[i] = int(cn.BRID)
	}
	minBRID := 1
	if as.Type == INFRASTRUCTURE {
		minBRID += config.RESERVED_BRS_INFRASTRUCTURE
	}
	id, err := utility.GetFreeID(brIDs, minBRID, config.MAX_BR_ID)
	return uint16(id), err
}

// TODO(mlegner): Avoid signed/unsigned casting; could be problematic if huge IP ranges are used
func (as *SCIONLabAS) GetFreeVPNIP() (string, error) {
	cns, err := as.getRespondConnections()
	if err != nil {
		return "", fmt.Errorf("Error finding connections of AP %v: %v", as.IA(), err)
	}
	vpnIPs := []int{}
	for _, cn := range cns {
		if cn.IsVPN {
			vpnIPs = append(vpnIPs, int(utility.IPToInt(cn.JoinIP)))
{'fraction_non_alphanumeric': 0.11718027734976888, 'fraction_numerical': 0.00970724191063174, 'mean_word_length': 3.0603690960275256, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 2, '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': '6231499', 'n_tokens_mistral': 4487, 'n_tokens_neox': 4344, 'n_words': 1409}