Commit ac565b19 authored by wu's avatar wu

add upf-n4

parent 3e0ae965
# Created by .ignore support plugin (hsz.mobi)
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="dataSourceStorageLocal">
<data-source name="n4" uuid="7e4f659a-4bf0-4452-8335-c266af6bc8a1">
<database-info product="SQLite" version="3.31.1" jdbc-version="2.1" driver-name="SQLite JDBC" driver-version="3.31.1" dbms="SQLITE" exact-version="3.31.1" exact-driver-version="3.31">
<identifier-quote-string>&quot;</identifier-quote-string>
</database-info>
<case-sensitivity plain-identifiers="mixed" quoted-identifiers="mixed" />
<secret-storage>master_key</secret-storage>
<auth-provider>no-auth</auth-provider>
<schema-mapping>
<introspection-scope>
<node kind="schema" qname="@" />
</introspection-scope>
</schema-mapping>
</data-source>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
<data-source source="LOCAL" name="n4" uuid="7e4f659a-4bf0-4452-8335-c266af6bc8a1">
<driver-ref>sqlite.xerial</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
<jdbc-url>jdbc:sqlite:$PROJECT_DIR$/n4.db</jdbc-url>
</data-source>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<dataSource name="n4">
<database-model serializer="dbm" dbms="SQLITE" family-id="SQLITE" format-version="4.18">
<root id="1">
<ServerVersion>3.31.1</ServerVersion>
</root>
<schema id="2" parent="1" name="main">
<Current>1</Current>
</schema>
<collation id="3" parent="1" name="BINARY"/>
<collation id="4" parent="1" name="NOCASE"/>
<collation id="5" parent="1" name="RTRIM"/>
<table id="6" parent="2" name="association"/>
<table id="7" parent="2" name="sqlite_master">
<System>1</System>
</table>
<table id="8" parent="2" name="sqlite_sequence">
<System>1</System>
</table>
<column id="9" parent="6" name="id">
<Position>1</Position>
<DataType>INTEGER|0s</DataType>
<NotNull>1</NotNull>
<SequenceIdentity>1</SequenceIdentity>
</column>
<column id="10" parent="6" name="i_pv4">
<Position>2</Position>
<DataType>TEXT|0s</DataType>
</column>
<column id="11" parent="6" name="flags">
<Position>3</Position>
<DataType>INTEGER|0s</DataType>
</column>
<column id="12" parent="6" name="version">
<Position>4</Position>
<DataType>INTEGER|0s</DataType>
<DefaultExpression>1</DefaultExpression>
</column>
<index id="13" parent="6" name="UQE_association_i_pv4">
<ColNames>i_pv4</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<key id="14" parent="6">
<ColNames>id</ColNames>
<Primary>1</Primary>
</key>
<column id="15" parent="7" name="type">
<Position>1</Position>
<DataType>text|0s</DataType>
</column>
<column id="16" parent="7" name="name">
<Position>2</Position>
<DataType>text|0s</DataType>
</column>
<column id="17" parent="7" name="tbl_name">
<Position>3</Position>
<DataType>text|0s</DataType>
</column>
<column id="18" parent="7" name="rootpage">
<Position>4</Position>
<DataType>int|0s</DataType>
</column>
<column id="19" parent="7" name="sql">
<Position>5</Position>
<DataType>text|0s</DataType>
</column>
<column id="20" parent="8" name="name">
<Position>1</Position>
</column>
<column id="21" parent="8" name="seq">
<Position>2</Position>
</column>
</database-model>
</dataSource>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptSettings">
<option name="languageLevel" value="ES6" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/upf-n4.iml" filepath="$PROJECT_DIR$/.idea/upf-n4.iml" />
</modules>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>
\ No newline at end of file
This diff is collapsed.
# 0.4 UPF-N4
## Test process and results of relevant functions
In UPF version 0.4, the following two processes were tested:
1. Create session - > delete session
![image-20200526132301230](doc/picture/image-20200526132301230.png)
2. Create session - > Modify session - > delete session
![image-20200526132611642](doc/picture/image-20200526132611642.png)
## 0.5 Functions to be improved in the version
UPF version 0.4 does not support the update and release of coupling, storage of session information, processing of multiple nodes and other functions. The above functions will be supported in version 0.5.
Important note: the IE completed above is based on the standard that Wireshark packet capturing can be parsed normally, not that its process can be handled normally in UPF. If you need to replicate the test process, please refer to the pcap package attached to the project for the message content to be encapsulated.
## Use guide
```shell script
cd example/upf/main
go build main.go
sudo ./main --upf upf upf的IP --smf smf的IP
```
\ No newline at end of file
#!/bin/bash
BASE="/opt/5gc/support-system/start_5g.sh"
echo "close UDM"
$BASE --close_udm
echo "close SMF"
$BASE --close_smf
echo "close AMF"
$BASE --close_amf
echo "close VPP"
$BASE --close_vpp
echo "close N4"
$BASE --close_n4
echo "close GNB"
$BASE --close_gnb
\ No newline at end of file
// Copyright 2019-2020 UPF-N4 authors. All rights reserved.
// Use of this source code is governed by a MIT-style license that can be
// found in the LICENSE file.
package main
import "C"
import (
"flag"
"n4/conf"
"n4/pkg/log"
"n4/pkg/upf/client"
"n4/pkg/upf/controller"
"os"
)
//var c conf.Config
var (
add = flag.String("c", "/home/wu/GOPATH/src/n4/conf/n4_config.yaml", "n4 configure file address")
)
func main() {
flag.Parse()
log.Info("UPF-N4 Start up\n")
end := make(chan bool)
c := conf.Config{}
config := c.GetConf(*add)
exist, err := pathExists("/var/log/upf/5gn4")
if err != nil {
log.Error("Git log file dir error ")
}
if exist {
err := os.Remove("/var/log/upf/5gn4/logrus.log")
if err != nil {
log.Error("Log file remove failed!")
}
err = os.MkdirAll("/var/log/upf/5gn4", os.ModePerm)
if err != nil {
log.Error("Log file created failed")
}
} else {
err = os.MkdirAll("/var/log/upf/5gn4", os.ModePerm)
if err != nil {
log.Error("Log file created failed")
}
}
go controller.UDPServer(config)
go client.UPDClient(config)
<-end
}
func pathExists(path string) (bool, error) {
_, err := os.Stat(path)
if err == nil {
return true, nil
}
if os.IsNotExist(err) {
return false, nil
}
return false, err
}
#!/bin/bash
rm -rf main
go build
./main -c /home/wu/go/src/upf-n4/conf/conf.yaml
\ No newline at end of file
// Copyright 2019-2020 UPF-N4 authors. All rights reserved.
// Use of this source code is governed by a MIT-style license that can be
// found in the LICENSE file.
package conf
import (
"gopkg.in/yaml.v2"
"io/ioutil"
"n4/pkg/log"
)
type Config struct {
Host string `yaml:"host"`
Port string `yaml:"port"`
Gnb string `yaml:"gnb"`
Smf string `yaml:"smf"`
Upf string `yaml:"upf"`
LogSize string `yaml:"logSize"`
}
func (c *Config) GetConf(confAddress string) *Config {
yamlFile, err := ioutil.ReadFile(confAddress)
if err != nil {
log.Error(err.Error())
}
err = yaml.Unmarshal(yamlFile, c)
if err != nil {
log.Error(err.Error())
}
return c
}
gnb: 192.168.10.10
smf: 192.168.2.63
upf: 192.168.2.135
logSize: 100
\ No newline at end of file
#!/bin/bash
#echo $1
#echo $2
#vppctl -s /run/vpp/cli.sock create gtpu tunnel src 192.168.10.10 dst "$1" teid "$2" encap-vrf-id 0 decap-next node ip4-lookup
function create_tunnel() {
gnb_gtpu="$1"
teid="$2"
vppctl -s /run/vpp/cli.sock create gtpu tunnel src 192.168.10.10 dst $gnb_gtpu teid $teid encap-vrf-id 0 decap-next node ip4-lookup
exit 0
}
function ip_route() {
ue_ip="$1"
gtpu_name="$2"
vppctl -s /run/vpp/cli.sock ip route add $ue_ip/32 via $gtpu_name
exit 0
}
function main() {
until [ -z "$1" ]; do
case "$1" in
--create_tunnel)
create_tunnel "$2" "$3"
shift
;;
--ip_route)
ip_route "$2" "$3"
shift
;;
esac
done
exit 0
}
main "$@"
package main
import (
"encoding/binary"
"flag"
"fmt"
"net"
"os"
"time"
)
var host = flag.String("host", "localhost", "host")
var port = flag.String("port", "37", "port")
func main() {
flag.Parse()
addr, err := net.ResolveUDPAddr("udp", *host+":"+*port)
if err != nil {
fmt.Println("Can't resolve address: ", err)
os.Exit(1)
}
conn, err := net.DialUDP("udp", nil, addr)
if err != nil {
fmt.Println("Can't dial: ", err)
os.Exit(1)
}
defer conn.Close()
_, err = conn.Write([]byte(""))
if err != nil {
fmt.Println("failed:", err)
os.Exit(1)
}
data := make([]byte, 4)
_, err = conn.Read(data)
if err != nil {
fmt.Println("failed to read UDP msg because of ", err)
os.Exit(1)
}
t := binary.BigEndian.Uint32(data)
fmt.Println(time.Unix(int64(t), 0).String())
os.Exit(0)
}
func establishmentRequest() []byte {
return nil
}
func modificationRequestCreate() []byte {
return nil
}
func modificationRequestUpdate() []byte {
return nil
}
#!/bin/bash
rm -rf cmd
go build
scp cmd vpp.sh root@192.168.2.234:/opt/5gc/support-system
package main
import (
"fmt"
"os"
"os/exec"
)
func main() {
var (
err error
out []byte
)
cmd := exec.Command("/opt/5gc/support-system/vpp_config.sh", "--create_tunnel", "192.168.10.200", "2")
if out, err = cmd.Output(); err != nil {
fmt.Println(err)
os.Exit(1)
}
fmt.Println("tunnel name:" + string(out))
}
#!/bin/bash
#echo $1
#echo $2
#vppctl -s /run/vpp/cli.sock create gtpu tunnel src 192.168.10.10 dst "$1" teid "$2" encap-vrf-id 0 decap-next node ip4-lookup
function create_tunnel() {
gnb_gtpu="$1"
teid="$2"
vppctl -s /run/vpp/cli.sock create gtpu tunnel src 192.168.10.10 dst $gnb_gtpu teid $teid encap-vrf-id 0 decap-next node ip4-lookup
exit 0
}
function ip_route() {
ue_ip="$1"
gtpu_name="$2"
vppctl -s /run/vpp/cli.sock ip route add $ue_ip/32 via $gtpu_name
exit 0
}
function main() {
until [ -z "$1" ]; do
case "$1" in
--create_tunnel)
create_tunnel "$2" "$3"
shift
;;
--ip_route)
ip_route "$2" "$3"
shift
;;
esac
done
exit 0
}
main "$@"
// Copyright 2019-2020 UPF-N4 authors. All rights reserved.
// Use of this source code is governed by a MIT-style license that can be
// found in the LICENSE file.
package main
import "database/sql"
import _ "github.com/go-sql-driver/mysql"
func main() {
db, _ := sql.Open("mysql", "root:123456@/dbname")
defer db.Close()
}
// Copyright 2019-2020 UPF-N4 authors. All rights reserved.
// Use of this source code is governed by a MIT-style license that can be
// found in the LICENSE file.
package main
import (
"flag"
"log"
"net"
"time"
"n4/pkg/pfcp/ie"
"n4/pkg/pfcp/message"
)
func main() {
var (
server = flag.String("-s", "127.0.0.1:8805", "controller's addr/port")
)
flag.Parse()
raddr, err := net.ResolveUDPAddr("udp", *server)
if err != nil {
log.Fatal(err)
}
conn, err := net.DialUDP("udp", nil, raddr)
if err != nil {
log.Fatal(err)
}
//IE required for encapsulation
nodeID := ie.NewNodeID("172.55.55.102", "", "")
acreq, err := message.NewAssociationReleaseRequest(0, 0, 0, nodeID).Marshal()
if err != nil {
log.Fatal(err)
}
if _, err := conn.Write(acreq); err != nil {
log.Fatal(err)
}
log.Printf("sent Association Release Request to: %s", raddr)
if err := conn.SetReadDeadline(time.Now().Add(3 * time.Second)); err != nil {
log.Fatal(err)
}
buf := make([]byte, 1500)
waiting := true
for waiting {
n, addr, err := conn.ReadFrom(buf)
if err != nil {
log.Fatal(err)
}
msg, err := message.Parse(buf[:n])
if err != nil {
log.Printf("ignored undecodable message: %x, error: %s", buf[:n], err)
continue
}
acres, ok := msg.(*message.AssociationReleaseResponse)
if !ok {
log.Printf("got unexpected message: %s, from: %s", msg.MessageTypeName(), addr)
continue
}
waiting = false
nid, err := acres.NodeID.NodeID()
if err != nil {
log.Printf("got Association Release Response with invalid NodeID: %s, from: %s", err, addr)
break
}
log.Printf("got Association Release Response with NodeID: %s, from: %s", nid, addr)
}
}
// Copyright 2019-2020 UPF-N4 authors. All rights reserved.
// Use of this source code is governed by a MIT-style license that can be
// found in the LICENSE file.
package main
import (
"flag"
"n4/pkg/pfcp/ie"
_ "n4/pkg/pfcp/ie"
"go_n4/n4/log"
"n4/pkg/pfcp/message"
"net"
)
func main() {
var (
listen = flag.String("-s", "127.0.0.1:8805", "addr/port to listen on")
)
flag.Parse()
laddr, err := net.ResolveUDPAddr("udp", *listen)
if err != nil {
log.Fatal(err.Error())
}
conn, err := net.ListenUDP("udp", laddr)
if err != nil {
log.Fatal(err.Error())
}
buf := make([]byte, 1500)
for {
log.Info("waiting for messages to come on: " + laddr.String())
n, addr, err := conn.ReadFrom(buf)
if err != nil {
log.Fatal(err.Error())
}
msg, err := message.Parse(buf[:n])
log.Info(msg.MessageTypeName())
acreq, ok := msg.(*message.AssociationReleaseRequest)
if !ok {
log.Info("got unexpected message: s" + msg.MessageTypeName())
continue
}
nid, err := acreq.NodeID.NodeID()
if err != nil {
log.Info("got Association Release Request with invalid NodeID: " + err.Error())
continue
} else {
log.Info("got Association Release Request with NodeID: " + nid)
}
nodeID := ie.NewNodeID("172.55.55.102", "", "")
cause := ie.NewCause(0)
// Timestamp shouldn't be the time message is sent in the real deployment but anyway :D
acres, err := message.NewAssociationReleaseResponse(0, 0, 0, nodeID, cause).Marshal()
if err != nil {
log.Fatal(err.Error())
}
if _, err := conn.WriteTo(acres, addr); err != nil {
log.Fatal(err.Error())
}
log.Info("sent Association Release Response to: " + addr.String())
}
}
// Copyright 2019-2020 UPF-N4 authors. All rights reserved.
// Use of this source code is governed by a MIT-style license that can be
// found in the LICENSE file.
package main
import (
"flag"
"log"
"net"
"time"
"n4/pkg/pfcp/ie"
"n4/pkg/pfcp/message"
)
func main() {
var (
server = flag.String("-s", "127.0.0.1:8805", "controller's addr/port")
)
flag.Parse()
raddr, err := net.ResolveUDPAddr("udp", *server)
if err != nil {
log.Fatal(err)
}
conn, err := net.DialUDP("udp", nil, raddr)
if err != nil {
log.Fatal(err)
}
//IE required for encapsulation
nodeID := ie.NewNodeID("172.55.55.102", "", "")
ts := ie.NewRecoveryTimeStamp(time.Date(2020, 3, 3, 12, 22, 22, 22, time.Local))
up := ie.NewUPFunctionFeatures(0x10, 0x00)
cp := ie.NewCPFunctionFeatures(0x10)
pfcpAssociationReleaseRequest := ie.NewPFCPAssociationReleaseRequest(1, 2)
gracefulReleasePeriod := ie.NewGracefulReleasePeriod(time.Microsecond)
pfcpaureqFlags := ie.NewPFCPAUReqFlags(true)
ipv4 := net.ParseIP("192.168.2.146").To4()
ipv6 := net.ParseIP("2001:db8:130F:0000:0000:09C0:876A:130B").To16()
userPlaneIPResourceInformation1 := ie.NewUserPlaneIpResourceInformation(0, ipv4, ipv6, "111", 10)
userPlaneIPResourceInformation2 := ie.NewUserPlaneIpResourceInformation(0, ipv4, ipv6, "111", 10)
alternativeSMFIPAddress1 := ie.NewAlternativeSMFIPAddress(ipv4, ipv6)
alternativeSMFIPAddress2 := ie.NewAlternativeSMFIPAddress(ipv4, ipv6)
acreq, err := message.NewAssociationUpdateRequest(0, 0, 0, nodeID, ts, up, cp, userPlaneIPResourceInformation1, userPlaneIPResourceInformation2, alternativeSMFIPAddress1, alternativeSMFIPAddress2, pfcpAssociationReleaseRequest, gracefulReleasePeriod, pfcpaureqFlags).Marshal()
if err != nil {
log.Fatal(err)
}
if _, err := conn.Write(acreq); err != nil {
log.Fatal(err)
}
log.Printf("sent Association Update Request to: %s", raddr)
if err := conn.SetReadDeadline(time.Now().Add(3 * time.Second)); err != nil {
log.Fatal(err)
}
buf := make([]byte, 1500)
waiting := true
for waiting {
n, addr, err := conn.ReadFrom(buf)
if err != nil {
log.Fatal(err)
}
msg, err := message.Parse(buf[:n])
if err != nil {
log.Printf("ignored undecodable message: %x, error: %s", buf[:n], err)
continue
}
acres, ok := msg.(*message.AssociationSetupResponse)
if !ok {
log.Printf("got unexpected message: %s, from: %s", msg.MessageTypeName(), addr)
continue
}
waiting = false
nid, err := acres.NodeID.NodeID()
if err != nil {
log.Printf("got Association Update Response with invalid NodeID: %s, from: %s", err, addr)
break
}
log.Printf("got Association Update Response with NodeID: %s, from: %s", nid, addr)
}
}
// Copyright 2019-2020 UPF-N4 authors. All rights reserved.
// Use of this source code is governed by a MIT-style license that can be
// found in the LICENSE file.
package main
import (
"flag"
"log"
"n4/pkg/pfcp/ie"
"n4/pkg/pfcp/message"
"net"
)
func main() {
var (
listen = flag.String("-s", "127.0.0.1:8805", "addr/port to listen on")
)
flag.Parse()
laddr, err := net.ResolveUDPAddr("udp", *listen)
if err != nil {
log.Fatal(err)
}
conn, err := net.ListenUDP("udp", laddr)
if err != nil {
log.Fatal(err)
}
buf := make([]byte, 1500)
for {
log.Printf("waiting for messages to come on: %s", laddr)
n, addr, err := conn.ReadFrom(buf)
if err != nil {
log.Fatal(err)
}
msg, err := message.Parse(buf[:n])
if err != nil {
log.Printf("ignored undecodable message: %x, error: %s", buf[:n], err)
continue
}
acreq, ok := msg.(*message.AssociationUpdateRequest)
if !ok {
log.Printf("got unexpected message: %s, from: %s", msg.MessageTypeName(), addr)
continue
}
nid, err := acreq.NodeID.NodeID()
if err != nil {
log.Printf("got Association Update Request with invalid NodeID: %s, from: %s", err, addr)
continue
} else {
log.Printf("got Association Update Request with NodeID: %s, from: %s", nid, addr)
}
nodeID := ie.NewNodeID("172.55.55.102", "", "")
cause := ie.NewCause(1)
up := ie.NewUPFunctionFeatures(0x10, 0x00)
cp := ie.NewCPFunctionFeatures(0x10)
// Timestamp shouldn't be the time message is sent in the real deployment but anyway :D
acres, err := message.NewAssociationUpdateResponse(0, 0, 0, nodeID, up, cp, cause).Marshal()
if err != nil {
log.Fatal(err)
}
if _, err := conn.WriteTo(acres, addr); err != nil {
log.Fatal(err)
}
log.Printf("sent Association Update Response to: %s", addr)
}
}
// Copyright 2019-2020 UPF-N4 authors. All rights reserved.
// Use of this source code is governed by a MIT-style license that can be
// found in the LICENSE file.
package main
import (
"flag"
"log"
"net"
"time"
"n4/pkg/pfcp/ie"
"n4/pkg/pfcp/message"
)
func main() {
var (
server = flag.String("-s", "127.0.0.1:8805", "controller's addr/port")
)
flag.Parse()
raddr, err := net.ResolveUDPAddr("udp", *server)
if err != nil {
log.Fatal(err)
}
conn, err := net.DialUDP("udp", nil, raddr)
if err != nil {
log.Fatal(err)
}
//IE required for encapsulation
nodeID := ie.NewNodeID("192.168.2.64", "", "")
ts := ie.NewRecoveryTimeStamp(time.Date(2020, 3, 3, 12, 22, 22, 22, time.Local))
up := ie.NewUPFunctionFeatures(0x10, 0x00)
cp := ie.NewUPFunctionFeatures(16, 0x00)
ipv4 := net.ParseIP("192.168.2.146").To4()
ipv6 := net.ParseIP("2001:db8:130F:0000:0000:09C0:876A:130B").To16()
userPlaneIPResourceInformation1 := ie.NewUserPlaneIpResourceInformation(0, ipv4, ipv6, "111", 10)
userPlaneIPResourceInformation2 := ie.NewUserPlaneIpResourceInformation(0, ipv4, ipv6, "111", 10)
alternativeSMFIPAddress1 := ie.NewAlternativeSMFIPAddress(ipv4, ipv6)
alternativeSMFIPAddress2 := ie.NewAlternativeSMFIPAddress(ipv4, ipv6)
acreq, err := message.NewAssociationSetupRequest(0, 0, 0, nodeID, ts, cp, up, userPlaneIPResourceInformation1, userPlaneIPResourceInformation2, alternativeSMFIPAddress1, alternativeSMFIPAddress2).Marshal()
if err != nil {
log.Fatal(err)
}
if _, err := conn.Write(acreq); err != nil {
log.Fatal(err)
}
log.Printf("sent AssociationSetup Request to: %s", raddr)
if err := conn.SetReadDeadline(time.Now().Add(3 * time.Second)); err != nil {
log.Fatal(err)
}
buf := make([]byte, 1500)
waiting := true
for waiting {
n, addr, err := conn.ReadFrom(buf)
if err != nil {
log.Fatal(err)
}
msg, err := message.Parse(buf[:n])
if err != nil {
log.Printf("ignored undecodable message: %x, error: %s", buf[:n], err)
continue
}
acres, ok := msg.(*message.AssociationSetupResponse)
if !ok {
log.Printf("got unexpected message: %s, from: %s", msg.MessageTypeName(), addr)
continue
}
waiting = false
nid, _ := acres.NodeID.NodeID()
cause, _ := acres.Cause.Cause()
ts, _ := acres.RecoveryTimeStamp.RecoveryTimeStamp()
cp, _ := acres.CPFunctionFeatures.CPFunctionFeatures()
log.Printf("got Association Setup Response with NodeID: %s, Cause: %d,RecoveryTimeStamp: %s,CPFunctionFeatures: %d", nid, cause, ts, cp)
}
}
// Copyright 2019-2020 UPF-N4 authors. All rights reserved.
// Use of this source code is governed by a MIT-style license that can be
// found in the LICENSE file.
package main
import (
"flag"
"log"
"n4/pkg/pfcp/ie"
"n4/pkg/pfcp/message"
"net"
"time"
)
func main() {
var (
listen = flag.String("-s", "127.0.0.1:8805", "addr/port to listen on")
)
flag.Parse()
laddr, err := net.ResolveUDPAddr("udp", *listen)
if err != nil {
log.Fatal(err)
}
conn, err := net.ListenUDP("udp", laddr)
if err != nil {
log.Fatal(err)
}
buf := make([]byte, 1500)
for {
log.Printf("waiting for messages to come on: %s", laddr)
n, addr, err := conn.ReadFrom(buf)
if err != nil {
log.Fatal(err)
}
msg, err := message.Parse(buf[:n])
if err != nil {
log.Printf("ignored undecodable message: %x, error: %s", buf[:n], err)
continue
}
acreq, ok := msg.(*message.AssociationSetupRequest)
if !ok {
log.Printf("got unexpected message: %s, from: %s", msg.MessageTypeName(), addr)
continue
}
nid, _ := acreq.NodeID.NodeID()
ts, _ := acreq.RecoveryTimeStamp.RecoveryTimeStamp()
cp, _ := acreq.CPFunctionFeatures.CPFunctionFeatures()
log.Printf("got Association Setup Request with NodeID: %s,RecoveryTimeStamp: %s , CPFunctionFeatures: %d", nid, ts, cp)
nodeID := ie.NewNodeID("172.55.55.102", "", "")
cause := ie.NewCause(1)
ts_1 := ie.NewRecoveryTimeStamp(time.Date(2020, 3, 3, 12, 22, 22, 22, time.Local))
cp_1 := ie.NewCPFunctionFeatures(0x00)
//// Timestamp shouldn't be the time message is sent in the real deployment but anyway :D
acres, err := message.NewAssociationSetupResponse(0, 0, 0, nodeID, ts_1, cp_1, cause).Marshal()
if err != nil {
log.Fatal(err)
}
if _, err := conn.WriteTo(acres, addr); err != nil {
log.Fatal(err)
}
log.Printf("sent Association Setup Response to: %s", addr)
}
}
// Copyright 2019-2020 UPF-N4 authors. All rights reserved.
// Use of this source code is governed by a MIT-style license that can be
// found in the LICENSE file.
// Command hb-client sends a HeartbeatRequest and checks response.
//
// Heartbeat exchanging feature is planned be included in the go-pfcp package's
// built-in functions in the future.
package main
import (
"flag"
"log"
"net"
"time"
"n4/pkg/pfcp/ie"
"n4/pkg/pfcp/message"
)
func main() {
var (
server = flag.String("-s", "127.0.0.1:8805", "controller's addr/port")
)
flag.Parse()
raddr, err := net.ResolveUDPAddr("udp", *server)
if err != nil {
log.Fatal(err)
}
conn, err := net.DialUDP("udp", nil, raddr)
if err != nil {
log.Fatal(err)
}
hbreq, err := message.NewHeartbeatRequest(0, 0, 0, ie.NewRecoveryTimeStamp(time.Now())).Marshal()
if err != nil {
log.Fatal(err)
}
if _, err := conn.Write(hbreq); err != nil {
log.Fatal(err)
}
log.Printf("sent Heartbeat Request to: %s", raddr)
if err := conn.SetReadDeadline(time.Now().Add(3 * time.Second)); err != nil {
log.Fatal(err)
}
buf := make([]byte, 1500)
waiting := true
for waiting {
n, addr, err := conn.ReadFrom(buf)
if err != nil {
log.Fatal(err)
}
msg, err := message.Parse(buf[:n])
if err != nil {
log.Printf("ignored undecodable message: %x, error: %s", buf[:n], err)
continue
}
hbres, ok := msg.(*message.HeartbeatResponse)
if !ok {
log.Printf("got unexpected message: %s, from: %s", msg.MessageTypeName(), addr)
continue
}
waiting = false
ts, err := hbres.RecoveryTimeStamp.RecoveryTimeStamp()
if err != nil {
log.Printf("got Heartbeat Response with invalid TS: %s, from: %s", err, addr)
break
}
log.Printf("got Heartbeat Response with TS: %s, from: %s", ts, addr)
}
}
// Copyright 2019-2020 UPF-N4 authors. All rights reserved.
// Use of this source code is governed by a MIT-style license that can be
// found in the LICENSE file.
// Command hb-controller sends a HeartbeatRequest and checks response.
//
// Heartbeat exchanging feature is planned be included in the go-pfcp package's
// built-in functions in the future.
package main
import (
"flag"
"log"
"n4/pkg/pfcp/ie"
"n4/pkg/pfcp/message"
"net"
"time"
)
func main() {
var (
listen = flag.String("-s", "127.0.0.2:8805", "addr/port to listen on")
)
flag.Parse()
laddr, err := net.ResolveUDPAddr("udp", *listen)
if err != nil {
log.Fatal(err)
}
conn, err := net.ListenUDP("udp", laddr)
if err != nil {
log.Fatal(err)
}
buf := make([]byte, 1500)
for {
log.Printf("waiting for messages to come on: %s", laddr)
n, addr, err := conn.ReadFrom(buf)
if err != nil {
log.Fatal(err)
}
msg, err := message.Parse(buf[:n])
if err != nil {
log.Printf("ignored undecodable message: %x, error: %s", buf[:n], err)
continue
}
hbreq, ok := msg.(*message.HeartbeatRequest)
if !ok {
log.Printf("got unexpected message: %s, from: %s", msg.MessageTypeName(), addr)
continue
}
ts, err := hbreq.RecoveryTimeStamp.RecoveryTimeStamp()
if err != nil {
log.Printf("got Heartbeat Request with invalid TS: %s, from: %s", err, addr)
continue
} else {
log.Printf("got Heartbeat Request with TS: %s, from: %s", ts, addr)
}
// Timestamp shouldn't be the time message is sent in the real deployment but anyway :D
hbres, err := message.NewHeartbeatResponse(0, 0, 0, ie.NewRecoveryTimeStamp(time.Now())).Marshal()
if err != nil {
log.Fatal(err)
}
if _, err := conn.WriteTo(hbres, addr); err != nil {
log.Fatal(err)
}
log.Printf("sent Heartbeat Response to: %s", addr)
}
}
// Copyright 2019-2020 UPF-N4 authors. All rights reserved.
// Use of this source code is governed by a MIT-style license that can be
// found in the LICENSE file.
package main
import (
"flag"
"log"
"n4/pkg/pfcp/ie"
"net"
"time"
"n4/pkg/pfcp/message"
)
func main() {
var (
server = flag.String("-s", "127.0.0.1:8805", "controller's addr/port")
)
flag.Parse()
raddr, err := net.ResolveUDPAddr("udp", *server)
if err != nil {
log.Fatal(err)
}
conn, err := net.DialUDP("udp", nil, raddr)
if err != nil {
log.Fatal(err)
}
//IE required for encapsulation
nodeID := ie.NewNodeID("192.168.2.1", "", "")
nodeReportType := ie.NewNodeReportType(1)
dl := ie.NewDestinationInterface(1)
nt := ie.NewNetworkInstance("11")
userPlanePathFailureReport := ie.NewUserPlanePathFailureReport(ie.NewRemoteGTPUPeer("192.168.2.1", "", dl, nt))
acreq, err := message.NewNodeReportRequest(0, 0, 0, nodeID, nodeReportType, userPlanePathFailureReport).Marshal()
if err != nil {
log.Fatal(err)
}
if _, err := conn.Write(acreq); err != nil {
log.Fatal(err)
}
log.Printf("sent PFCP Node Report Request to: %s", raddr)
if err := conn.SetReadDeadline(time.Now().Add(3 * time.Second)); err != nil {
log.Fatal(err)
}
buf := make([]byte, 1500)
waiting := true
for waiting {
n, addr, err := conn.ReadFrom(buf)
if err != nil {
log.Fatal(err)
}
msg, err := message.Parse(buf[:n])
if err != nil {
log.Printf("ignored undecodable message: %x, error: %s", buf[:n], err)
continue
}
acres, ok := msg.(*message.NodeReportResponse)
if !ok {
log.Printf("got unexpected message: %s, from: %s", msg.MessageTypeName(), addr)
continue
}
waiting = false
nid, err := acres.NodeID.NodeID()
if err != nil {
log.Printf("got PFCP Node Report Response with invalid NodeID: %s, from: %s", err, addr)
break
}
log.Printf("got PFCP Node Report Response with NodeID: %s, from: %s", nid, addr)
}
}
// Copyright 2019-2020 UPF-N4 authors. All rights reserved.
// Use of this source code is governed by a MIT-style license that can be
// found in the LICENSE file.
package main
import (
"flag"
"log"
"n4/pkg/pfcp/ie"
"n4/pkg/pfcp/message"
"net"
)
func main() {
var (
listen = flag.String("-s", "127.0.0.1:8805", "addr/port to listen on")
)
flag.Parse()
laddr, err := net.ResolveUDPAddr("udp", *listen)
if err != nil {
log.Fatal(err)
}
conn, err := net.ListenUDP("udp", laddr)
if err != nil {
log.Fatal(err)
}
buf := make([]byte, 1500)
for {
log.Printf("waiting for messages to come on: %s", laddr)
n, addr, err := conn.ReadFrom(buf)
if err != nil {
log.Fatal(err)
}
msg, err := message.Parse(buf[:n])
if err != nil {
log.Printf("ignored undecodable message: %x, error: %s", buf[:n], err)
continue
}
acreq, ok := msg.(*message.NodeReportRequest)
if !ok {
log.Printf("got unexpected message: %s, from: %s", msg.MessageTypeName(), addr)
continue
}
nid, err := acreq.NodeID.NodeID()
if err != nil {
log.Printf("got PFCP Node Report Request with invalid NodeID: %s, from: %s", err, addr)
continue
} else {
log.Printf("got PFCP Node Report Request with NodeID: %s, from: %s", nid, addr)
}
nodeID := ie.NewNodeID("172.55.55.102", "", "")
cause := ie.NewCause(0)
offendingIE := ie.NewOffendingIE(1)
// Timestamp shouldn't be the time message is sent in the real deployment but anyway :D
acres, err := message.NewNodeReportResponse(0, 0, 0, nodeID, cause, offendingIE).Marshal()
if err != nil {
log.Fatal(err)
}
if _, err := conn.WriteTo(acres, addr); err != nil {
log.Fatal(err)
}
log.Printf("sent PFCP Node Report Response to: %s", addr)
}
}
// Copyright 2019-2020 UPF-N4 authors. All rights reserved.
// Use of this source code is governed by a MIT-style license that can be
// found in the LICENSE file.
package main
import (
"flag"
"log"
"net"
"time"
"n4/pkg/pfcp/ie"
"n4/pkg/pfcp/message"
)
func main() {
var (
server = flag.String("-s", "127.0.0.1:8805", "controller's addr/port")
)
flag.Parse()
raddr, err := net.ResolveUDPAddr("udp", *server)
if err != nil {
log.Fatal(err)
}
conn, err := net.DialUDP("udp", nil, raddr)
if err != nil {
log.Fatal(err)
}
//IE required for encapsulation
pfdContents := ie.NewPFDContents("11", "192.168.2.130", "11", "11", "11", []string{"11", "22"}, nil, nil)
pfdContent := ie.NewPFDContext(pfdContents)
applictionID := ie.NewApplicationID("1")
applictionIDsPFDs := ie.NewApplicationIDsPFDs(applictionID, pfdContent)
acreq, err := message.NewPFDManagementRequest(1, 0, 0, applictionIDsPFDs).Marshal()
if err != nil {
log.Fatal(err)
}
if _, err := conn.Write(acreq); err != nil {
log.Fatal(err)
}
log.Printf("sent PFCP PFD Management Request to: %s", raddr)
if err := conn.SetReadDeadline(time.Now().Add(3 * time.Second)); err != nil {
log.Fatal(err)
}
}
// Copyright 2019-2020 UPF-N4 authors. All rights reserved.
// Use of this source code is governed by a MIT-style license that can be
// found in the LICENSE file.
package main
import (
"flag"
"log"
"n4/pkg/pfcp/ie"
"n4/pkg/pfcp/message"
"net"
)
func main() {
var (
listen = flag.String("-s", "127.0.0.1:8805", "addr/port to listen on")
)
flag.Parse()
laddr, err := net.ResolveUDPAddr("udp", *listen)
if err != nil {
log.Fatal(err)
}
conn, err := net.ListenUDP("udp", laddr)
if err != nil {
log.Fatal(err)
}
buf := make([]byte, 1500)
for {
log.Printf("waiting for messages to come on: %s", laddr)
n, addr, err := conn.ReadFrom(buf)
if err != nil {
log.Fatal(err)
}
msg, err := message.Parse(buf[:n])
if err != nil {
log.Printf("ignored undecodable message: %x, error: %s", buf[:n], err)
continue
}
_, ok := msg.(*message.PFDManagementRequest)
if !ok {
log.Printf("got unexpected message: %s, from: %s", msg.MessageTypeName(), addr)
continue
}
cause := ie.NewCause(1)
offendingIE := ie.NewOffendingIE(1)
// Timestamp shouldn't be the time message is sent in the real deployment but anyway :D
acres, err := message.NewPFDManagementResponse(0, 0, 0, cause, offendingIE).Marshal()
if err != nil {
log.Fatal(err)
}
if _, err := conn.WriteTo(acres, addr); err != nil {
log.Fatal(err)
}
log.Printf("sent PFD Management Response to: %s", addr)
}
}
// Copyright 2019-2020 UPF-N4 authors. All rights reserved.
// Use of this source code is governed by a MIT-style license that can be
// found in the LICENSE file.
package main
import (
"flag"
"log"
"n4/pkg/pfcp/ie"
"n4/pkg/pfcp/message"
"net"
"time"
)
func main() {
var (
server = flag.String("-s", "127.0.0.1:8805", "controller's addr/port")
)
flag.Parse()
raddr, err := net.ResolveUDPAddr("udp", *server)
if err != nil {
log.Fatal(err)
}
conn, err := net.DialUDP("udp", nil, raddr)
if err != nil {
log.Fatal(err)
}
f_seid := ie.NewFSEID(0xbebebebebebebebf, net.ParseIP("192.168.2.182"), nil, nil)
hbreq, err := message.NewSessionDeletionRequest(0, 0, 0, f_seid).Marshal()
if err != nil {
log.Fatal(err)
}
if _, err := conn.Write(hbreq); err != nil {
log.Fatal(err)
}
log.Printf("sent Session Deletion Request to: %s", raddr)
if err := conn.SetReadDeadline(time.Now().Add(3 * time.Second)); err != nil {
log.Fatal(err)
}
buf := make([]byte, 1500)
waiting := true
for waiting {
n, addr, err := conn.ReadFrom(buf)
if err != nil {
log.Fatal(err)
}
msg, err := message.Parse(buf[:n])
if err != nil {
log.Printf("ignored undecodable message: %x, error: %s", buf[:n], err)
continue
}
resp, ok := msg.(*message.SessionDeletionResponse)
if !ok {
log.Printf("got unexpected message: %s, from: %s", msg.MessageTypeName(), addr)
continue
}
waiting = false
cause, _ := resp.Cause.Cause()
offendingIE, _ := resp.OffendingIE.OffendingIE()
log.Printf("get message Cause: %d, Offending IE :%d ", cause, offendingIE)
}
}
// Copyright 2019-2020 UPF-N4 authors. All rights reserved.
// Use of this source code is governed by a MIT-style license that can be
// found in the LICENSE file.
package main
import (
"flag"
"log"
"n4/pkg/pfcp/ie"
"n4/pkg/pfcp/message"
"net"
)
func main() {
var (
listen = flag.String("-s", "127.0.0.1:8805", "addr/port to listen on")
)
flag.Parse()
laddr, err := net.ResolveUDPAddr("udp", *listen)
if err != nil {
log.Fatal(err)
}
conn, err := net.ListenUDP("udp", laddr)
if err != nil {
log.Fatal(err)
}
buf := make([]byte, 1500)
for {
log.Printf("waiting for messages to come on: %s", laddr)
n, addr, err := conn.ReadFrom(buf)
if err != nil {
log.Fatal(err)
}
msg, err := message.Parse(buf[:n])
if err != nil {
log.Printf("ignored undecodable message: %x, error: %s", buf[:n], err)
continue
}
req, ok := msg.(*message.SessionDeletionRequest)
if !ok {
log.Printf("got unexpected message: %s, from: %s", msg.MessageTypeName(), addr)
continue
}
fseid, _ := req.CPFSeid.FSEID()
log.Printf("get message F-SEID:%d", fseid.SEID)
cause := ie.NewCause(ie.CauseConditionalIEMissing)
offendingIE := ie.NewOffendingIE(2)
// Timestamp shouldn't be the time message is sent in the real deployment but anyway :D
acres, err := message.NewSessionDeletionResponse(0, 0, 0, cause, offendingIE).Marshal()
if err != nil {
log.Fatal(err)
}
if _, err := conn.WriteTo(acres, addr); err != nil {
log.Fatal(err)
}
log.Printf("sent PFD Deletion Response to: %s", addr)
}
}
// Copyright 2019-2020 UPF-N4 authors. All rights reserved.
// Use of this source code is governed by a MIT-style license that can be
// found in the LICENSE file.
package main
import (
"flag"
"log"
"n4/pkg/pfcp/ie"
"n4/pkg/pfcp/message"
"net"
"time"
)
func main() {
var (
server = flag.String("-s", "127.0.0.1:8805", "controller's addr/port")
)
flag.Parse()
raddr, err := net.ResolveUDPAddr("udp", *server)
if err != nil {
log.Fatal(err)
}
conn, err := net.DialUDP("udp", nil, raddr)
if err != nil {
log.Fatal(err)
}
nodeID := ie.NewNodeID("192.168.2.181", "", "")
f_seid := ie.NewFSEID(0xbebebebebebebebf, net.ParseIP("192.168.2.182"), nil, nil)
pdrID := ie.NewPacketDetectionRuleID(1)
precedence := ie.NewPrecedence(0)
sourceInterface := ie.NewSourceInterface(0)
f_teid := ie.NewFTEID(0, nil, nil, nil)
ue_ip_address := ie.NewUEIPAddress("10.0.0.3", "", 0)
pdi := ie.NewPDI(sourceInterface, f_teid, ue_ip_address)
far_id := ie.NewFARID(1)
create_pdr := ie.NewCreatePDR(pdrID, precedence, pdi, far_id)
applyAction := ie.NewApplyAction(0x02)
destinationInterface := ie.NewDestinationInterface(1)
outerHeaderCreation := ie.NewOuterHeaderCreation(ie.CTPUUDPIPv4, 1, "192.168.2.157", "", 0, 0, 0)
forwardingParameters := ie.NewForwardingParameters(0, destinationInterface, outerHeaderCreation)
create_far := ie.NewCreateFAR(far_id, applyAction, forwardingParameters)
hbreq, err := message.NewSessionEstablishmentRequest(0, 0, 0, nodeID, f_seid, create_pdr, create_far).Marshal()
if err != nil {
log.Fatal(err)
}
if _, err := conn.Write(hbreq); err != nil {
log.Fatal(err)
}
log.Printf("sent Session Establishment Request to: %s", raddr)
if err := conn.SetReadDeadline(time.Now().Add(3 * time.Second)); err != nil {
log.Fatal(err)
}
buf := make([]byte, 1500)
waiting := true
for waiting {
n, addr, err := conn.ReadFrom(buf)
if err != nil {
log.Fatal(err)
}
msg, err := message.Parse(buf[:n])
if err != nil {
log.Printf("ignored undecodable message: %x, error: %s", buf[:n], err)
continue
}
acres, ok := msg.(*message.SessionEstablishmentResponse)
if !ok {
log.Printf("got unexpected message: %s, from: %s", msg.MessageTypeName(), addr)
continue
}
waiting = false
nid, _ := acres.NodeID.NodeID()
cause, _ := acres.Cause.Cause()
ipv4, _ := acres.UPFSEID.FSEID()
if err != nil {
log.Printf("got Session Establishment Response with invalid NodeID: %s, from: %s", err, addr)
break
}
log.Printf("got Session Establishment Response with NodeID: %s, cause: %d,ipv4: %s,PDRID: %d,F TEID IPv4: %s", nid, cause, ipv4.IPv4Address)
}
}
// Copyright 2019-2020 UPF-N4 authors. All rights reserved.
// Use of this source code is governed by a MIT-style license that can be
// found in the LICENSE file.
package main
import (
"flag"
"log"
"n4/pkg/pfcp/ie"
"n4/pkg/pfcp/message"
"net"
)
func main() {
var (
listen = flag.String("-s", "127.0.0.1:8805", "addr/port to listen on")
)
flag.Parse()
laddr, err := net.ResolveUDPAddr("udp", *listen)
if err != nil {
log.Fatal(err)
}
conn, err := net.ListenUDP("udp", laddr)
if err != nil {
log.Fatal(err)
}
buf := make([]byte, 1500)
for {
log.Printf("waiting for messages to come on: %s", laddr)
n, addr, err := conn.ReadFrom(buf)
if err != nil {
log.Fatal(err)
}
msg, err := message.Parse(buf[:n])
if err != nil {
log.Printf("ignored undecodable message: %x, error: %s", buf[:n], err)
continue
}
req, ok := msg.(*message.SessionEstablishmentRequest)
if !ok {
log.Printf("got unexpected message: %s, from: %s", msg.MessageTypeName(), addr)
continue
}
nodeID, _ := req.NodeID.NodeID()
fseid, _ := req.CPFSeid.FSEID()
createPDR, _ := req.CreatePDR[0].CreatePDR()
pdrid, _ := createPDR[0].PacketDetectionRuleID()
precedence, _ := createPDR[1].Precedence()
pdiIEs, _ := createPDR[2].PDI()
sourceInterface, _ := pdiIEs[0].SourceInterface()
//fteid, _ := pdiIEs[1].FTEID()
ueipaddress, _ := pdiIEs[2].UEIPAddress()
farid, _ := createPDR[3].FARID()
createFAR, _ := req.CreateFAR[0].CreateFAR()
applyAction, _ := createFAR[1].ApplyAction()
forwardingParameters, _ := createFAR[2].ForwardingParameters()
destinationInterface, _ := forwardingParameters[0].DestinationInterface()
outerHeaderCreation, _ := forwardingParameters[1].OuterHeaderCreation()
log.Printf("get message Node ID: %s,F-SEID:%d,ipv4:%s,PDR ID: %d, Precedence: %d, Source Interface: %d,UE IP Address:%s, FAR ID:%d ", nodeID, fseid.SEID, fseid.IPv4Address, pdrid, precedence, sourceInterface, ueipaddress.IPv4Address, farid)
log.Printf("get message ApplyAction: %x,DestionationInterface:%d,OuterHeaderCreation IPv4:%s", applyAction, destinationInterface, outerHeaderCreation.IPv4Address)
nodeID_2 := ie.NewNodeID("192.168.2.115", "", "")
cause := ie.NewCause(ie.CauseRequestAccepted)
f_seid := ie.NewFSEID(1, net.ParseIP("192.168.2.115"), nil, nil)
pdrid_2 := ie.NewPacketDetectionRuleID(1)
f_teid := ie.NewFTEID(1, net.ParseIP("192.168.2.189"), nil, nil)
createPDR_2 := ie.NewCreatePDR(pdrid_2, f_teid)
// Timestamp shouldn't be the time message is sent in the real deployment but anyway :D
acres, err := message.NewSessionEstablishmentResponse(111111111111, 0, 0, nodeID_2, cause, f_seid, createPDR_2).Marshal()
if err != nil {
log.Fatal(err)
}
if _, err := conn.WriteTo(acres, addr); err != nil {
log.Fatal(err)
}
log.Printf("sent PFD Management Response to: %s", addr)
}
}
// Copyright 2019-2020 UPF-N4 authors. All rights reserved.
// Use of this source code is governed by a MIT-style license that can be
// found in the LICENSE file.
package main
import (
"flag"
"log"
"n4/pkg/pfcp/ie"
"n4/pkg/pfcp/message"
"net"
"time"
)
func main() {
var (
server = flag.String("-s", "127.0.0.1:8805", "controller's addr/port")
)
flag.Parse()
raddr, err := net.ResolveUDPAddr("udp", *server)
if err != nil {
log.Fatal(err)
}
conn, err := net.DialUDP("udp", nil, raddr)
if err != nil {
log.Fatal(err)
}
pdr_id := ie.NewPacketDetectionRuleID(1)
far_id := ie.NewFARID(1)
remove_pdr := ie.NewRemovePDR(pdr_id)
remove_far := ie.NewRemoveFAR(far_id)
f_seid := ie.NewFSEID(0xbebebebebebebebf, net.ParseIP("192.168.2.182"), nil, nil)
precedence := ie.NewPrecedence(0)
sourceInterface := ie.NewSourceInterface(0)
f_teid := ie.NewFTEID(0, nil, nil, nil)
ue_ip_address := ie.NewUEIPAddress("10.0.0.3", "", 0)
pdi := ie.NewPDI(sourceInterface, f_teid, ue_ip_address)
create_pdr := ie.NewUpdatePDR(pdr_id, precedence, pdi, far_id)
applyAction := ie.NewApplyAction(0x02)
destinationInterface := ie.NewDestinationInterface(1)
outerHeaderCreation := ie.NewOuterHeaderCreation(ie.CTPUUDPIPv4, 1, "192.168.2.157", "", 0, 0, 0)
forwardingParameters := ie.NewForwardingParameters(0, destinationInterface, outerHeaderCreation)
create_far := ie.NewUpdateFAR(far_id, applyAction, forwardingParameters)
hbreq, err := message.NewSessionModificationRequest(0, 0, 0, f_seid, remove_pdr, remove_far, create_pdr, create_far).Marshal()
if err != nil {
log.Fatal(err)
}
if _, err := conn.Write(hbreq); err != nil {
log.Fatal(err)
}
log.Printf("sent Session Modification Request to: %s", raddr)
if err := conn.SetReadDeadline(time.Now().Add(3 * time.Second)); err != nil {
log.Fatal(err)
}
buf := make([]byte, 1500)
waiting := true
for waiting {
n, addr, err := conn.ReadFrom(buf)
if err != nil {
log.Fatal(err)
}
msg, err := message.Parse(buf[:n])
if err != nil {
log.Printf("ignored undecodable message: %x, error: %s", buf[:n], err)
continue
}
resp, ok := msg.(*message.SessionModificationResponse)
if !ok {
log.Printf("got unexpected message: %s, from: %s", msg.MessageTypeName(), addr)
continue
}
waiting = false
cause, _ := resp.Cause.Cause()
offendingIE, _ := resp.OffendingIE.OffendingIE()
log.Printf("get message Cause: %d, Offending IE :%d ", cause, offendingIE)
}
}
// Copyright 2019-2020 UPF-N4 authors. All rights reserved.
// Use of this source code is governed by a MIT-style license that can be
// found in the LICENSE file.
package main
import (
"flag"
"log"
"n4/pkg/pfcp/ie"
"n4/pkg/pfcp/message"
"net"
)
func main() {
var (
listen = flag.String("-s", "127.0.0.1:8805", "addr/port to listen on")
)
flag.Parse()
laddr, err := net.ResolveUDPAddr("udp", *listen)
if err != nil {
log.Fatal(err)
}
conn, err := net.ListenUDP("udp", laddr)
if err != nil {
log.Fatal(err)
}
buf := make([]byte, 1500)
for {
log.Printf("waiting for messages to come on: %s", laddr)
n, addr, err := conn.ReadFrom(buf)
if err != nil {
log.Fatal(err)
}
msg, err := message.Parse(buf[:n])
if err != nil {
log.Printf("ignored undecodable message: %x, error: %s", buf[:n], err)
continue
}
req, ok := msg.(*message.SessionModificationRequest)
if !ok {
log.Printf("got unexpected message: %s, from: %s", msg.MessageTypeName(), addr)
continue
}
removePDRList, _ := req.RemovePDR[0].RemovePDR()
removePDR, _ := removePDRList[0].PacketDetectionRuleID()
removeFARList, _ := req.RemoveFAR[0].RemoveFAR()
removeFAR, _ := removeFARList[0].FARID()
fseid, _ := req.CPFSeid.FSEID()
updatePDR, _ := req.UpdatePDR[0].UpdatePDR()
pdrid, _ := updatePDR[0].PacketDetectionRuleID()
precedence, _ := updatePDR[1].Precedence()
pdiIEs, _ := updatePDR[2].PDI()
sourceInterface, _ := pdiIEs[0].SourceInterface()
//fteid, _ := pdiIEs[1].FTEID()
ueipaddress, _ := pdiIEs[2].UEIPAddress()
farid, _ := updatePDR[3].FARID()
updateFAR, _ := req.UpdateFAR[0].UpdateFAR()
applyAction, _ := updateFAR[1].ApplyAction()
forwardingParameters, _ := updateFAR[2].ForwardingParameters()
destinationInterface, _ := forwardingParameters[0].DestinationInterface()
outerHeaderCreation, _ := forwardingParameters[1].OuterHeaderCreation()
log.Printf("get message Remove PDR ID: %d, Remove FAR ID:%d ", removePDR, removeFAR)
log.Printf("get message F-SEID:%d,ipv4:%s,PDR ID: %d, Precedence: %d, Source Interface: %d,UE IP Address:%s, FAR ID:%d ", fseid.SEID, fseid.IPv4Address, pdrid, precedence, sourceInterface, ueipaddress.IPv4Address, farid)
log.Printf("get message ApplyAction: %x,DestionationInterface:%d,OuterHeaderCreation IPv4:%s", applyAction, destinationInterface, outerHeaderCreation.IPv4Address)
cause := ie.NewCause(ie.CauseConditionalIEMissing)
offendingIE := ie.NewOffendingIE(2)
// Timestamp shouldn't be the time message is sent in the real deployment but anyway :D
acres, err := message.NewSessionModificationResponse(0, 0, 0, cause, offendingIE).Marshal()
if err != nil {
log.Fatal(err)
}
if _, err := conn.WriteTo(acres, addr); err != nil {
log.Fatal(err)
}
log.Printf("sent PFD Management Response to: %s", addr)
}
}
This diff is collapsed.
// Code generated by GoVPP's binapi-generator. DO NOT EDIT.
// source: /usr/share/vpp/api/core/af_packet.api.json
/*
Package af_packet is a generated VPP binary API for 'af_packet' module.
It consists of:
8 messages
4 services
*/
package af_packet
import (
bytes "bytes"
context "context"
struc "github.com/lunixbochs/struc"
io "io"
api "n4/pkg/vpp/api"
strconv "strconv"
)
const (
// ModuleName is the name of this module.
ModuleName = "af_packet"
// APIVersion is the API version of this module.
APIVersion = "1.0.0"
// VersionCrc is the CRC of this module.
VersionCrc = 0xfefd69b7
)
// AfPacketCreate represents VPP binary API message 'af_packet_create'.
type AfPacketCreate struct {
HostIfName []byte `struc:"[64]byte"`
HwAddr []byte `struc:"[6]byte"`
UseRandomHwAddr uint8
}
func (*AfPacketCreate) GetMessageName() string {
return "af_packet_create"
}
func (*AfPacketCreate) GetCrcString() string {
return "6d5d30d6"
}
func (*AfPacketCreate) GetMessageType() api.MessageType {
return api.RequestMessage
}
// AfPacketCreateReply represents VPP binary API message 'af_packet_create_reply'.
type AfPacketCreateReply struct {
Retval int32
SwIfIndex uint32
}
func (*AfPacketCreateReply) GetMessageName() string {
return "af_packet_create_reply"
}
func (*AfPacketCreateReply) GetCrcString() string {
return "fda5941f"
}
func (*AfPacketCreateReply) GetMessageType() api.MessageType {
return api.ReplyMessage
}
// AfPacketDelete represents VPP binary API message 'af_packet_delete'.
type AfPacketDelete struct {
HostIfName []byte `struc:"[64]byte"`
}
func (*AfPacketDelete) GetMessageName() string {
return "af_packet_delete"
}
func (*AfPacketDelete) GetCrcString() string {
return "3efceda3"
}
func (*AfPacketDelete) GetMessageType() api.MessageType {
return api.RequestMessage
}
// AfPacketDeleteReply represents VPP binary API message 'af_packet_delete_reply'.
type AfPacketDeleteReply struct {
Retval int32
}
func (*AfPacketDeleteReply) GetMessageName() string {
return "af_packet_delete_reply"
}
func (*AfPacketDeleteReply) GetCrcString() string {
return "e8d4e804"
}
func (*AfPacketDeleteReply) GetMessageType() api.MessageType {
return api.ReplyMessage
}
// AfPacketDetails represents VPP binary API message 'af_packet_details'.
type AfPacketDetails struct {
SwIfIndex uint32
HostIfName []byte `struc:"[64]byte"`
}
func (*AfPacketDetails) GetMessageName() string {
return "af_packet_details"
}
func (*AfPacketDetails) GetCrcString() string {
return "057205fa"
}
func (*AfPacketDetails) GetMessageType() api.MessageType {
return api.ReplyMessage
}
// AfPacketDump represents VPP binary API message 'af_packet_dump'.
type AfPacketDump struct{}
func (*AfPacketDump) GetMessageName() string {
return "af_packet_dump"
}
func (*AfPacketDump) GetCrcString() string {
return "51077d14"
}
func (*AfPacketDump) GetMessageType() api.MessageType {
return api.RequestMessage
}
// AfPacketSetL4CksumOffload represents VPP binary API message 'af_packet_set_l4_cksum_offload'.
type AfPacketSetL4CksumOffload struct {
SwIfIndex uint8
Set uint8
}
func (*AfPacketSetL4CksumOffload) GetMessageName() string {
return "af_packet_set_l4_cksum_offload"
}
func (*AfPacketSetL4CksumOffload) GetCrcString() string {
return "86538585"
}
func (*AfPacketSetL4CksumOffload) GetMessageType() api.MessageType {
return api.RequestMessage
}
// AfPacketSetL4CksumOffloadReply represents VPP binary API message 'af_packet_set_l4_cksum_offload_reply'.
type AfPacketSetL4CksumOffloadReply struct {
Retval int32
}
func (*AfPacketSetL4CksumOffloadReply) GetMessageName() string {
return "af_packet_set_l4_cksum_offload_reply"
}
func (*AfPacketSetL4CksumOffloadReply) GetCrcString() string {
return "e8d4e804"
}
func (*AfPacketSetL4CksumOffloadReply) GetMessageType() api.MessageType {
return api.ReplyMessage
}
func init() {
api.RegisterMessage((*AfPacketCreate)(nil), "af_packet.AfPacketCreate")
api.RegisterMessage((*AfPacketCreateReply)(nil), "af_packet.AfPacketCreateReply")
api.RegisterMessage((*AfPacketDelete)(nil), "af_packet.AfPacketDelete")
api.RegisterMessage((*AfPacketDeleteReply)(nil), "af_packet.AfPacketDeleteReply")
api.RegisterMessage((*AfPacketDetails)(nil), "af_packet.AfPacketDetails")
api.RegisterMessage((*AfPacketDump)(nil), "af_packet.AfPacketDump")
api.RegisterMessage((*AfPacketSetL4CksumOffload)(nil), "af_packet.AfPacketSetL4CksumOffload")
api.RegisterMessage((*AfPacketSetL4CksumOffloadReply)(nil), "af_packet.AfPacketSetL4CksumOffloadReply")
}
// Messages returns list of all messages in this module.
func AllMessages() []api.Message {
return []api.Message{
(*AfPacketCreate)(nil),
(*AfPacketCreateReply)(nil),
(*AfPacketDelete)(nil),
(*AfPacketDeleteReply)(nil),
(*AfPacketDetails)(nil),
(*AfPacketDump)(nil),
(*AfPacketSetL4CksumOffload)(nil),
(*AfPacketSetL4CksumOffloadReply)(nil),
}
}
// RPCService represents RPC service API for af_packet module.
type RPCService interface {
DumpAfPacket(ctx context.Context, in *AfPacketDump) (RPCService_DumpAfPacketClient, error)
AfPacketCreate(ctx context.Context, in *AfPacketCreate) (*AfPacketCreateReply, error)
AfPacketDelete(ctx context.Context, in *AfPacketDelete) (*AfPacketDeleteReply, error)
AfPacketSetL4CksumOffload(ctx context.Context, in *AfPacketSetL4CksumOffload) (*AfPacketSetL4CksumOffloadReply, error)
}
type serviceClient struct {
ch api.Channel
}
func NewServiceClient(ch api.Channel) RPCService {
return &serviceClient{ch}
}
func (c *serviceClient) DumpAfPacket(ctx context.Context, in *AfPacketDump) (RPCService_DumpAfPacketClient, error) {
stream := c.ch.SendMultiRequest(in)
x := &serviceClient_DumpAfPacketClient{stream}
return x, nil
}
type RPCService_DumpAfPacketClient interface {
Recv() (*AfPacketDetails, error)
}
type serviceClient_DumpAfPacketClient struct {
api.MultiRequestCtx
}
func (c *serviceClient_DumpAfPacketClient) Recv() (*AfPacketDetails, error) {
m := new(AfPacketDetails)
stop, err := c.MultiRequestCtx.ReceiveReply(m)
if err != nil {
return nil, err
}
if stop {
return nil, io.EOF
}
return m, nil
}
func (c *serviceClient) AfPacketCreate(ctx context.Context, in *AfPacketCreate) (*AfPacketCreateReply, error) {
out := new(AfPacketCreateReply)
err := c.ch.SendRequest(in).ReceiveReply(out)
if err != nil {
return nil, err
}
return out, nil
}
func (c *serviceClient) AfPacketDelete(ctx context.Context, in *AfPacketDelete) (*AfPacketDeleteReply, error) {
out := new(AfPacketDeleteReply)
err := c.ch.SendRequest(in).ReceiveReply(out)
if err != nil {
return nil, err
}
return out, nil
}
func (c *serviceClient) AfPacketSetL4CksumOffload(ctx context.Context, in *AfPacketSetL4CksumOffload) (*AfPacketSetL4CksumOffloadReply, error) {
out := new(AfPacketSetL4CksumOffloadReply)
err := c.ch.SendRequest(in).ReceiveReply(out)
if err != nil {
return nil, err
}
return out, nil
}
// This is a compile-time assertion to ensure that this generated file
// is compatible with the GoVPP api package it is being compiled against.
// A compilation error at this line likely means your copy of the
// GoVPP api package needs to be updated.
const _ = api.GoVppAPIPackageIsVersion1 // please upgrade the GoVPP api package
// Reference imports to suppress errors if they are not otherwise used.
var _ = api.RegisterMessage
var _ = bytes.NewBuffer
var _ = context.Background
var _ = io.Copy
var _ = strconv.Itoa
var _ = struc.Pack
package binapi
// Generate Go code from the VPP APIs located in the /usr/share/vpp/api directory.
//go:generate -command binapigen binapi-generator --output-dir=. --include-services --continue-onerror
//go:generate binapigen --input-file=/usr/share/vpp/api/core/af_packet.api.json
//go:generate binapigen --input-file=/usr/share/vpp/api/core/interface.api.json
//go:generate binapigen --input-file=/usr/share/vpp/api/core/ip.api.json
//go:generate binapigen --input-file=/usr/share/vpp/api/core/memclnt.api.json
//go:generate binapigen --input-file=/usr/share/vpp/api/core/vpe.api.json
//go:generate binapigen --input-file=/usr/share/vpp/api/plugins/memif.api.json
// VPP version
///go:generate sh -c "dpkg-query -f '$DOLLAR{Version}' -W vpp > VPP_VERSION"
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
// Copyright (c) 2017 Cisco and/or its affiliates.
//
// 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.
// Binary simple-unixsocket is an example VPP management application that exercises the
// govpp API on real-world use-cases.
package main
import (
"flag"
"fmt"
"log"
"time"
"github.com/pkg/profile"
"github.com/sirupsen/logrus"
"n4/pkg/vpp/adapter/socketclient"
"n4/pkg/vpp/adapter/statsclient"
"n4/pkg/vpp/api"
"n4/pkg/vpp/core"
"n4/pkg/vpp/examples/binapi/vpe"
)
const (
defaultSyncRequestCount = 1000
defaultAsyncRequestCount = 10000
)
func main() {
// parse optional flags
var sync, prof bool
var cnt int
var sock string
flag.BoolVar(&sync, "sync", false, "run synchronous perf test")
flag.StringVar(&sock, "socket", socketclient.DefaultSocketName, "Path to VPP API socket")
flag.String("socket", statsclient.DefaultSocketName, "Path to VPP stats socket")
flag.IntVar(&cnt, "count", 0, "count of requests to be sent to VPP")
flag.BoolVar(&prof, "prof", false, "generate profile data")
flag.Parse()
if cnt == 0 {
// no specific count defined - use defaults
if sync {
cnt = defaultSyncRequestCount
} else {
cnt = defaultAsyncRequestCount
}
}
if prof {
defer profile.Start().Stop()
}
a := socketclient.NewVppClient(sock)
// connect to VPP
conn, err := core.Connect(a)
if err != nil {
log.Fatalln("Error:", err)
}
defer conn.Disconnect()
// create an API channel
ch, err := conn.NewAPIChannelBuffered(cnt, cnt)
if err != nil {
log.Fatalln("Error:", err)
}
defer ch.Close()
ch.SetReplyTimeout(time.Second * 2)
// log only errors
core.SetLogger(&logrus.Logger{Level: logrus.ErrorLevel})
// run the test & measure the time
start := time.Now()
if sync {
// run synchronous test
syncTest(ch, cnt)
} else {
// run asynchronous test
asyncTest(ch, cnt)
}
elapsed := time.Since(start)
fmt.Println("Test took:", elapsed)
fmt.Printf("Requests per second: %.0f\n", float64(cnt)/elapsed.Seconds())
time.Sleep(time.Second)
}
func syncTest(ch api.Channel, cnt int) {
fmt.Printf("Running synchronous perf test with %d requests...\n", cnt)
for i := 0; i < cnt; i++ {
req := &vpe.ControlPing{}
reply := &vpe.ControlPingReply{}
if err := ch.SendRequest(req).ReceiveReply(reply); err != nil {
log.Fatalln("Error in reply:", err)
}
}
}
func asyncTest(ch api.Channel, cnt int) {
fmt.Printf("Running asynchronous perf test with %d requests...\n", cnt)
ctxChan := make(chan api.RequestCtx, cnt)
go func() {
for i := 0; i < cnt; i++ {
ctxChan <- ch.SendRequest(&vpe.ControlPing{})
}
close(ctxChan)
fmt.Printf("Sending asynchronous requests finished\n")
}()
for ctx := range ctxChan {
reply := &vpe.ControlPingReply{}
if err := ctx.ReceiveReply(reply); err != nil {
log.Fatalln("Error in reply:", err)
}
}
}
// Copyright (c) 2017 Cisco and/or its affiliates.
//
// 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.
// service-unixsocket is an example VPP management application that exercises the
// govpp API using generated service unixsocket.
package main
import (
"context"
"flag"
"fmt"
"io"
"log"
"n4/pkg/vpp/examples/binapi/memclnt"
"strings"
"n4/example/vpp/binapi/interfaces"
"n4/pkg/vpp"
"n4/pkg/vpp/adapter/socketclient"
"n4/pkg/vpp/api"
"n4/pkg/vpp/examples/binapi/vpe"
)
var (
sockAddr = flag.String("sock", socketclient.DefaultSocketName, "Path to VPP binary API socket file")
)
func main() {
flag.Parse()
fmt.Println("Starting RPC service example")
// connect to VPP
conn, err := govpp.Connect(*sockAddr)
if err != nil {
log.Fatalln("ERROR: connecting to VPP failed:", err)
}
defer conn.Disconnect()
// create a channel
ch, err := conn.NewAPIChannel()
if err != nil {
log.Fatalln("ERROR: creating channel failed:", err)
}
defer ch.Close()
showVersion(ch)
interfaceDump(ch)
}
// showVersion shows an example of simple request with services.
func showVersion(ch api.Channel) {
c := vpe.NewServiceClient(ch)
version, err := c.ShowVersion(context.Background(), &vpe.ShowVersion{})
if err != nil {
log.Fatalln("ERROR: ShowVersion failed:", err)
}
fmt.Printf("Version: %v\n", version.Version)
}
// interfaceDump shows an example of multi request with services.
func interfaceDump(ch api.Channel) {
c := interfaces.NewServiceClient(ch)
stream, err := c.DumpSwInterface(context.Background(), &interfaces.SwInterfaceDump{})
if err != nil {
log.Fatalln("ERROR: DumpSwInterface failed:", err)
}
fmt.Println("Dumping interfaces")
for {
iface, err := stream.Recv()
if err == io.EOF {
break
}
if err != nil {
log.Fatalln("ERROR: DumpSwInterface failed:", err)
}
fmt.Printf("- interface: %s\n", strings.Trim(iface.InterfaceName, "\x00"))
}
}
func traces(ch api.Channel) {
c := interfaces.NewServiceClient(ch)
stream, err := c.DumpSwInterface(context.Background(), &memclnt.TracePluginMsgIds{})
if err != nil {
log.Fatalln("ERROR: DumpSwInterface failed:", err)
}
fmt.Println("Dumping interfaces")
for {
iface, err := stream.Recv()
if err == io.EOF {
break
}
if err != nil {
log.Fatalln("ERROR: DumpSwInterface failed:", err)
}
fmt.Printf("- interface: %s\n", strings.Trim(iface.InterfaceName, "\x00"))
}
}
This diff is collapsed.
// Copyright 2019-2020 UPF-N4 authors. All rights reserved.
// Use of this source code is governed by a MIT-style license that can be
// found in the LICENSE file.
package main
func main() {
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Wed Aug 12 20:06:08 2020 -0700
#!/bin/bash
commit_id=`git rev-parse HEAD`
commit_date=`git log --pretty=format:"%cd" $commit_id -1`
sed -i "1 i\\$commit_date" n4.log
cat n4.log
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment