diff options
author | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2015-07-28 12:59:28 +0900 |
---|---|---|
committer | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2015-07-28 12:59:28 +0900 |
commit | 427e9efa79b3cac87a5628698ef0662088fd856a (patch) | |
tree | 5e508afbab87096899e6c07fc121489279f6a9bf | |
parent | 9abc68f4e7327ce856a1e05aac64c576c26c1258 (diff) |
add RPKI support
Just get info from ROA server. Not varidate any route yet.
Currently, "--rpki-server" option enables RPKI:
$ gobgpd --rpki-server 210.173.170.254:323
We'll use the configuration file for this later.
You can see ROAs via CLI:
$ gobgp rpki
For ipv6,
$ gobgp rpki -a v6
Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
-rw-r--r-- | api/gobgp.pb.go | 72 | ||||
-rw-r--r-- | api/gobgp.proto | 8 | ||||
-rw-r--r-- | gobgp/common.go | 17 | ||||
-rw-r--r-- | gobgp/main.go | 3 | ||||
-rw-r--r-- | gobgp/rpki.go | 77 | ||||
-rw-r--r-- | gobgpd/main.go | 3 | ||||
-rw-r--r-- | server/grpc_server.go | 21 | ||||
-rw-r--r-- | server/rpki.go | 121 | ||||
-rw-r--r-- | server/server.go | 8 |
9 files changed, 327 insertions, 3 deletions
diff --git a/api/gobgp.pb.go b/api/gobgp.pb.go index 218f1629..0d425fd6 100644 --- a/api/gobgp.pb.go +++ b/api/gobgp.pb.go @@ -53,6 +53,7 @@ It has these top-level messages: PolicyDefinition ApplyPolicy MrtMessage + ROA */ package api @@ -1377,6 +1378,17 @@ func (m *MrtMessage) Reset() { *m = MrtMessage{} } func (m *MrtMessage) String() string { return proto.CompactTextString(m) } func (*MrtMessage) ProtoMessage() {} +type ROA struct { + As uint32 `protobuf:"varint,1,opt,name=as" json:"as,omitempty"` + Prefixlen uint32 `protobuf:"varint,2,opt,name=prefixlen" json:"prefixlen,omitempty"` + Maxlen uint32 `protobuf:"varint,3,opt,name=maxlen" json:"maxlen,omitempty"` + Prefix string `protobuf:"bytes,4,opt,name=prefix" json:"prefix,omitempty"` +} + +func (m *ROA) Reset() { *m = ROA{} } +func (m *ROA) String() string { return proto.CompactTextString(m) } +func (*ROA) ProtoMessage() {} + func init() { proto.RegisterEnum("api.Resource", Resource_name, Resource_value) proto.RegisterEnum("api.Operation", Operation_name, Operation_value) @@ -1418,6 +1430,7 @@ type GrpcClient interface { MonitorBestChanged(ctx context.Context, in *Arguments, opts ...grpc.CallOption) (Grpc_MonitorBestChangedClient, error) MonitorPeerState(ctx context.Context, in *Arguments, opts ...grpc.CallOption) (Grpc_MonitorPeerStateClient, error) GetMrt(ctx context.Context, in *MrtArguments, opts ...grpc.CallOption) (Grpc_GetMrtClient, error) + GetRPKI(ctx context.Context, in *Arguments, opts ...grpc.CallOption) (Grpc_GetRPKIClient, error) } type grpcClient struct { @@ -1838,6 +1851,38 @@ func (x *grpcGetMrtClient) Recv() (*MrtMessage, error) { return m, nil } +func (c *grpcClient) GetRPKI(ctx context.Context, in *Arguments, opts ...grpc.CallOption) (Grpc_GetRPKIClient, error) { + stream, err := grpc.NewClientStream(ctx, &_Grpc_serviceDesc.Streams[10], c.cc, "/api.Grpc/GetRPKI", opts...) + if err != nil { + return nil, err + } + x := &grpcGetRPKIClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type Grpc_GetRPKIClient interface { + Recv() (*ROA, error) + grpc.ClientStream +} + +type grpcGetRPKIClient struct { + grpc.ClientStream +} + +func (x *grpcGetRPKIClient) Recv() (*ROA, error) { + m := new(ROA) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + // Server API for Grpc service type GrpcServer interface { @@ -1861,6 +1906,7 @@ type GrpcServer interface { MonitorBestChanged(*Arguments, Grpc_MonitorBestChangedServer) error MonitorPeerState(*Arguments, Grpc_MonitorPeerStateServer) error GetMrt(*MrtArguments, Grpc_GetMrtServer) error + GetRPKI(*Arguments, Grpc_GetRPKIServer) error } func RegisterGrpcServer(s *grpc.Server, srv GrpcServer) { @@ -2212,6 +2258,27 @@ func (x *grpcGetMrtServer) Send(m *MrtMessage) error { return x.ServerStream.SendMsg(m) } +func _Grpc_GetRPKI_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(Arguments) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(GrpcServer).GetRPKI(m, &grpcGetRPKIServer{stream}) +} + +type Grpc_GetRPKIServer interface { + Send(*ROA) error + grpc.ServerStream +} + +type grpcGetRPKIServer struct { + grpc.ServerStream +} + +func (x *grpcGetRPKIServer) Send(m *ROA) error { + return x.ServerStream.SendMsg(m) +} + var _Grpc_serviceDesc = grpc.ServiceDesc{ ServiceName: "api.Grpc", HandlerType: (*GrpcServer)(nil), @@ -2310,5 +2377,10 @@ var _Grpc_serviceDesc = grpc.ServiceDesc{ Handler: _Grpc_GetMrt_Handler, ServerStreams: true, }, + { + StreamName: "GetRPKI", + Handler: _Grpc_GetRPKI_Handler, + ServerStreams: true, + }, }, } diff --git a/api/gobgp.proto b/api/gobgp.proto index 5441647c..1a573977 100644 --- a/api/gobgp.proto +++ b/api/gobgp.proto @@ -40,6 +40,7 @@ service Grpc { rpc MonitorBestChanged(Arguments) returns (stream Path) {} rpc MonitorPeerState(Arguments) returns (stream Peer) {} rpc GetMrt(MrtArguments) returns (stream MrtMessage) {} + rpc GetRPKI(Arguments) returns (stream ROA) {} } message Error { @@ -497,3 +498,10 @@ message ApplyPolicy { message MrtMessage { bytes data = 1; } + +message ROA { + uint32 as = 1; + uint32 prefixlen = 2; + uint32 maxlen = 3; + string prefix = 4; +}
\ No newline at end of file diff --git a/gobgp/common.go b/gobgp/common.go index b65a7662..29928bac 100644 --- a/gobgp/common.go +++ b/gobgp/common.go @@ -58,6 +58,7 @@ const ( CMD_MRT = "mrt" CMD_DUMP = "dump" CMD_INJECT = "inject" + CMD_RPKI = "rpki" ) var subOpts struct { @@ -271,6 +272,22 @@ func (p policyDefinitions) Less(i, j int) bool { return p[i].PolicyDefinitionName < p[j].PolicyDefinitionName } +type roas []*api.ROA + +func (r roas) Len() int { + return len(r) +} + +func (r roas) Swap(i, j int) { + r[i], r[j] = r[j], r[i] +} + +func (r roas) Less(i, j int) bool { + strings := sort.StringSlice{cidr2prefix(fmt.Sprintf("%s/%d", r[i].Prefix, r[i].Prefixlen)), + cidr2prefix(fmt.Sprintf("%s/%d", r[j].Prefix, r[j].Prefixlen))} + return strings.Less(0, 1) +} + func connGrpc() *grpc.ClientConn { timeout := grpc.WithTimeout(time.Second) diff --git a/gobgp/main.go b/gobgp/main.go index 0878c593..08134c4b 100644 --- a/gobgp/main.go +++ b/gobgp/main.go @@ -60,6 +60,7 @@ func main() { policyCmd := NewPolicyCmd() monitorCmd := NewMonitorCmd() mrtCmd := NewMrtCmd() - rootCmd.AddCommand(globalCmd, neighborCmd, policyCmd, monitorCmd, mrtCmd) + rpkiCmd := NewRPKICmd() + rootCmd.AddCommand(globalCmd, neighborCmd, policyCmd, monitorCmd, mrtCmd, rpkiCmd) rootCmd.Execute() } diff --git a/gobgp/rpki.go b/gobgp/rpki.go new file mode 100644 index 00000000..9ec500d7 --- /dev/null +++ b/gobgp/rpki.go @@ -0,0 +1,77 @@ +// Copyright (C) 2015 Nippon Telegraph and Telephone Corporation. +// +// 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 main + +import ( + "fmt" + "github.com/osrg/gobgp/api" + "github.com/osrg/gobgp/packet" + "github.com/spf13/cobra" + "golang.org/x/net/context" + "io" + "sort" + "net" + "os" +) + +func showRPKITable(args []string) error { + af, err := checkAddressFamily(net.IP{}) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + arg := &api.Arguments{ + Af: af, + } + stream, err := client.GetRPKI(context.Background(), arg) + if err != nil { + fmt.Println(err) + return err + } + l := roas{} + for { + r, err := stream.Recv() + if err == io.EOF { + break + } else if err != nil { + return err + } + l = append(l, r) + } + sort.Sort(l) + var format string + if af.Afi == bgp.AFI_IP { + format = "%-18s %-6s %s\n" + } else { + format = "%-42s %-6s %s\n" + } + fmt.Printf(format, "Network", "Maxlen", "AS") + for _, r := range l { + fmt.Printf(format, fmt.Sprintf("%s/%d", r.Prefix, r.Prefixlen), fmt.Sprint(r.Maxlen), fmt.Sprint(r.Maxlen)) + } + return nil +} + +func NewRPKICmd() *cobra.Command { + rpkiCmd := &cobra.Command{ + Use: CMD_RPKI, + Run: func(cmd *cobra.Command, args []string) { + showRPKITable(args) + }, + } + rpkiCmd.PersistentFlags().StringVarP(&subOpts.AddressFamily, "address-family", "a", "", "address family") + return rpkiCmd +} diff --git a/gobgpd/main.go b/gobgpd/main.go index 89cd0cb8..9d96401b 100644 --- a/gobgpd/main.go +++ b/gobgpd/main.go @@ -46,6 +46,7 @@ func main() { DisableStdlog bool `long:"disable-stdlog" description:"disable standard logging"` EnableZapi bool `short:"z" long:"enable-zapi" description:"enable zebra api"` ZapiURL string `long:"zapi-url" description:"specify zebra api url"` + RPKIServer string `long:"rpki-server" description:"specify rpki server url"` } _, err := flags.Parse(&opts) if err != nil { @@ -141,7 +142,7 @@ func main() { reloadCh := make(chan bool) go config.ReadConfigfileServe(opts.ConfigFile, configCh, reloadCh) reloadCh <- true - bgpServer := server.NewBgpServer(bgp.BGP_PORT) + bgpServer := server.NewBgpServer(bgp.BGP_PORT, opts.RPKIServer) go bgpServer.Serve() // start grpc Server diff --git a/server/grpc_server.go b/server/grpc_server.go index 4a652c4f..a4e61be7 100644 --- a/server/grpc_server.go +++ b/server/grpc_server.go @@ -83,6 +83,7 @@ const ( REQ_MONITOR_GLOBAL_BEST_CHANGED REQ_MONITOR_NEIGHBOR_PEER_STATE REQ_MRT_GLOBAL_RIB + REQ_RPKI ) const GRPC_PORT = 8080 @@ -621,6 +622,26 @@ END: return err } +func (s *Server) GetRPKI(arg *api.Arguments, stream api.Grpc_GetRPKIServer) error { + rf, err := convertAf2Rf(arg.Af) + if err != nil { + return err + } + req := NewGrpcRequest(REQ_RPKI, "", rf, nil) + s.bgpServerCh <- req + + for res := range req.ResponseCh { + if err := res.Err(); err != nil { + log.Debug(err.Error()) + return err + } + if err := stream.Send(res.Data.(*api.ROA)); err != nil { + return err + } + } + return nil +} + type GrpcRequest struct { RequestType int RemoteAddr string diff --git a/server/rpki.go b/server/rpki.go new file mode 100644 index 00000000..6ad6aa0a --- /dev/null +++ b/server/rpki.go @@ -0,0 +1,121 @@ +// Copyright (C) 2015 Nippon Telegraph and Telephone Corporation. +// +// 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 server + +import ( + "bufio" + "fmt" + "github.com/osrg/gobgp/api" + "github.com/osrg/gobgp/packet" + "net" +) + +type roa struct { + AS uint32 + PrefixLen uint8 + MaxLen uint8 + Prefix net.IP +} + +func (r *roa) key() string { + return fmt.Sprintf("%s/%d", r.Prefix.String(), r.PrefixLen) +} + +func (r *roa) toApiStruct() *api.ROA { + return &api.ROA{ + As: r.AS, + Prefixlen: uint32(r.PrefixLen), + Maxlen: uint32(r.MaxLen), + Prefix: r.Prefix.String(), + } +} + +type roaClient struct { + roas map[bgp.RouteFamily]map[string]*roa + outgoing chan *roa +} + +func (c *roaClient) recieveROA() chan *roa { + return c.outgoing +} + +func (c *roaClient) handleRTRMsg(r *roa) { + if r.Prefix.To4() != nil { + c.roas[bgp.RF_IPv4_UC][r.key()] = r + } else { + c.roas[bgp.RF_IPv6_UC][r.key()] = r + } +} + +func (c *roaClient) handleGRPC(grpcReq *GrpcRequest) { + if roas, ok := c.roas[grpcReq.RouteFamily]; ok { + for _, r := range roas { + result := &GrpcResponse{} + result.Data = r.toApiStruct() + grpcReq.ResponseCh <- result + } + } + close(grpcReq.ResponseCh) +} + +func newROAClient(url string) (*roaClient, error) { + c := &roaClient{ + roas: make(map[bgp.RouteFamily]map[string]*roa), + } + c.roas[bgp.RF_IPv4_UC] = make(map[string]*roa) + c.roas[bgp.RF_IPv6_UC] = make(map[string]*roa) + + if url == "" { + return c, nil + } + + conn, err := net.Dial("tcp", url) + if err != nil { + return c, err + } + + r := bgp.NewRTRResetQuery() + data, _ := r.Serialize() + conn.Write(data) + reader := bufio.NewReader(conn) + scanner := bufio.NewScanner(reader) + scanner.Split(bgp.SplitRTR) + + ch := make(chan *roa) + c.outgoing = ch + + go func(ch chan *roa) { + for scanner.Scan() { + m, _ := bgp.ParseRTR(scanner.Bytes()) + if m != nil { + switch msg := m.(type) { + case *bgp.RTRIPPrefix: + p := make([]byte, len(msg.Prefix)) + copy(p, msg.Prefix) + ch <- &roa{ + AS: msg.AS, + PrefixLen: msg.PrefixLen, + MaxLen: msg.MaxLen, + Prefix: p, + } + } + + } + } + }(ch) + + return c, nil +} diff --git a/server/server.go b/server/server.go index ef0b2ce3..dfc7058a 100644 --- a/server/server.go +++ b/server/server.go @@ -79,9 +79,10 @@ type BgpServer struct { neighborMap map[string]*Peer localRibMap map[string]*LocalRib zclient *zebra.Client + roaClient *roaClient } -func NewBgpServer(port int) *BgpServer { +func NewBgpServer(port int, roaURL string) *BgpServer { b := BgpServer{} b.globalTypeCh = make(chan config.Global) b.addedPeerCh = make(chan config.Neighbor) @@ -91,6 +92,7 @@ func NewBgpServer(port int) *BgpServer { b.localRibMap = make(map[string]*LocalRib) b.neighborMap = make(map[string]*Peer) b.listenPort = port + b.roaClient, _ = newROAClient(roaURL) return &b } @@ -214,6 +216,8 @@ func (server *BgpServer) Serve() { } select { + case rmsg := <-server.roaClient.recieveROA(): + server.roaClient.handleRTRMsg(rmsg) case zmsg := <-zapiMsgCh: handleZapiMsg(zmsg) case conn := <-acceptCh: @@ -1289,6 +1293,8 @@ func (server *BgpServer) handleGrpc(grpcReq *GrpcRequest) []*SenderMsg { server.broadcastReqs = append(server.broadcastReqs, grpcReq) case REQ_MRT_GLOBAL_RIB: server.handleMrt(grpcReq) + case REQ_RPKI: + server.roaClient.handleGRPC(grpcReq) default: errmsg := fmt.Errorf("Unknown request type: %v", grpcReq.RequestType) result := &GrpcResponse{ |