diff options
-rw-r--r-- | api/gobgp.pb.go | 242 | ||||
-rw-r--r-- | api/gobgp.proto | 28 | ||||
-rw-r--r-- | gobgp/main.go | 384 | ||||
-rw-r--r-- | server/grpc_server.go | 115 | ||||
-rw-r--r-- | server/server.go | 184 |
5 files changed, 901 insertions, 52 deletions
diff --git a/api/gobgp.pb.go b/api/gobgp.pb.go index e552e363..a67af8f7 100644 --- a/api/gobgp.pb.go +++ b/api/gobgp.pb.go @@ -12,6 +12,7 @@ It has these top-level messages: Error Arguments ModPathArguments + PolicyArguments AddressFamily GracefulRestartTuple GracefulRestart @@ -30,6 +31,8 @@ It has these top-level messages: PeerConf PeerInfo Peer + Prefix + PrefixSet */ package api @@ -50,10 +53,11 @@ var _ = proto.Marshal type Resource int32 const ( - Resource_GLOBAL Resource = 0 - Resource_LOCAL Resource = 1 - Resource_ADJ_IN Resource = 2 - Resource_ADJ_OUT Resource = 3 + Resource_GLOBAL Resource = 0 + Resource_LOCAL Resource = 1 + Resource_ADJ_IN Resource = 2 + Resource_ADJ_OUT Resource = 3 + Resource_POLICY_PREFIX Resource = 4 ) var Resource_name = map[int32]string{ @@ -61,18 +65,43 @@ var Resource_name = map[int32]string{ 1: "LOCAL", 2: "ADJ_IN", 3: "ADJ_OUT", + 4: "POLICY_PREFIX", } var Resource_value = map[string]int32{ - "GLOBAL": 0, - "LOCAL": 1, - "ADJ_IN": 2, - "ADJ_OUT": 3, + "GLOBAL": 0, + "LOCAL": 1, + "ADJ_IN": 2, + "ADJ_OUT": 3, + "POLICY_PREFIX": 4, } func (x Resource) String() string { return proto.EnumName(Resource_name, int32(x)) } +type Operation int32 + +const ( + Operation_ADD Operation = 0 + Operation_DEL Operation = 1 + Operation_DEL_ALL Operation = 2 +) + +var Operation_name = map[int32]string{ + 0: "ADD", + 1: "DEL", + 2: "DEL_ALL", +} +var Operation_value = map[string]int32{ + "ADD": 0, + "DEL": 1, + "DEL_ALL": 2, +} + +func (x Operation) String() string { + return proto.EnumName(Operation_name, int32(x)) +} + type AFI int32 const ( @@ -476,6 +505,24 @@ func (m *ModPathArguments) GetPath() *Path { return nil } +type PolicyArguments struct { + Resource Resource `protobuf:"varint,1,opt,name=resource,enum=api.Resource" json:"resource,omitempty"` + Operation Operation `protobuf:"varint,2,opt,name=operation,enum=api.Operation" json:"operation,omitempty"` + Name string `protobuf:"bytes,3,opt,name=name" json:"name,omitempty"` + PrefixSet *PrefixSet `protobuf:"bytes,4,opt,name=prefix_set" json:"prefix_set,omitempty"` +} + +func (m *PolicyArguments) Reset() { *m = PolicyArguments{} } +func (m *PolicyArguments) String() string { return proto.CompactTextString(m) } +func (*PolicyArguments) ProtoMessage() {} + +func (m *PolicyArguments) GetPrefixSet() *PrefixSet { + if m != nil { + return m.PrefixSet + } + return nil +} + type AddressFamily struct { Afi AFI `protobuf:"varint,1,opt,enum=api.AFI" json:"Afi,omitempty"` Safi SAFI `protobuf:"varint,2,opt,enum=api.SAFI" json:"Safi,omitempty"` @@ -848,8 +895,35 @@ func (m *Peer) GetInfo() *PeerInfo { return nil } +type Prefix struct { + Address string `protobuf:"bytes,1,opt,name=address" json:"address,omitempty"` + MaskLength uint32 `protobuf:"varint,2,opt,name=mask_length" json:"mask_length,omitempty"` + MaskLengthRange string `protobuf:"bytes,3,opt,name=mask_length_range" json:"mask_length_range,omitempty"` +} + +func (m *Prefix) Reset() { *m = Prefix{} } +func (m *Prefix) String() string { return proto.CompactTextString(m) } +func (*Prefix) ProtoMessage() {} + +type PrefixSet struct { + PrefixSetName string `protobuf:"bytes,1,opt,name=prefix_set_name" json:"prefix_set_name,omitempty"` + PrefixList []*Prefix `protobuf:"bytes,2,rep,name=prefix_list" json:"prefix_list,omitempty"` +} + +func (m *PrefixSet) Reset() { *m = PrefixSet{} } +func (m *PrefixSet) String() string { return proto.CompactTextString(m) } +func (*PrefixSet) ProtoMessage() {} + +func (m *PrefixSet) GetPrefixList() []*Prefix { + if m != nil { + return m.PrefixList + } + return nil +} + func init() { proto.RegisterEnum("api.Resource", Resource_name, Resource_value) + proto.RegisterEnum("api.Operation", Operation_name, Operation_value) proto.RegisterEnum("api.AFI", AFI_name, AFI_value) proto.RegisterEnum("api.SAFI", SAFI_name, SAFI_value) proto.RegisterEnum("api.BGP_CAPABILITY", BGP_CAPABILITY_name, BGP_CAPABILITY_value) @@ -878,6 +952,9 @@ type GrpcClient interface { Enable(ctx context.Context, in *Arguments, opts ...grpc.CallOption) (*Error, error) Disable(ctx context.Context, in *Arguments, opts ...grpc.CallOption) (*Error, error) ModPath(ctx context.Context, opts ...grpc.CallOption) (Grpc_ModPathClient, error) + GetPolicyPrefixes(ctx context.Context, in *PolicyArguments, opts ...grpc.CallOption) (Grpc_GetPolicyPrefixesClient, error) + GetPolicyPrefix(ctx context.Context, in *PolicyArguments, opts ...grpc.CallOption) (*PrefixSet, error) + ModPolicyPrefix(ctx context.Context, opts ...grpc.CallOption) (Grpc_ModPolicyPrefixClient, error) } type grpcClient struct { @@ -1087,6 +1164,78 @@ func (x *grpcModPathClient) Recv() (*Error, error) { return m, nil } +func (c *grpcClient) GetPolicyPrefixes(ctx context.Context, in *PolicyArguments, opts ...grpc.CallOption) (Grpc_GetPolicyPrefixesClient, error) { + stream, err := grpc.NewClientStream(ctx, &_Grpc_serviceDesc.Streams[4], c.cc, "/api.Grpc/GetPolicyPrefixes", opts...) + if err != nil { + return nil, err + } + x := &grpcGetPolicyPrefixesClient{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_GetPolicyPrefixesClient interface { + Recv() (*PrefixSet, error) + grpc.ClientStream +} + +type grpcGetPolicyPrefixesClient struct { + grpc.ClientStream +} + +func (x *grpcGetPolicyPrefixesClient) Recv() (*PrefixSet, error) { + m := new(PrefixSet) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *grpcClient) GetPolicyPrefix(ctx context.Context, in *PolicyArguments, opts ...grpc.CallOption) (*PrefixSet, error) { + out := new(PrefixSet) + err := grpc.Invoke(ctx, "/api.Grpc/GetPolicyPrefix", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *grpcClient) ModPolicyPrefix(ctx context.Context, opts ...grpc.CallOption) (Grpc_ModPolicyPrefixClient, error) { + stream, err := grpc.NewClientStream(ctx, &_Grpc_serviceDesc.Streams[5], c.cc, "/api.Grpc/ModPolicyPrefix", opts...) + if err != nil { + return nil, err + } + x := &grpcModPolicyPrefixClient{stream} + return x, nil +} + +type Grpc_ModPolicyPrefixClient interface { + Send(*PolicyArguments) error + Recv() (*Error, error) + grpc.ClientStream +} + +type grpcModPolicyPrefixClient struct { + grpc.ClientStream +} + +func (x *grpcModPolicyPrefixClient) Send(m *PolicyArguments) error { + return x.ClientStream.SendMsg(m) +} + +func (x *grpcModPolicyPrefixClient) Recv() (*Error, error) { + m := new(Error) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + // Server API for Grpc service type GrpcServer interface { @@ -1102,6 +1251,9 @@ type GrpcServer interface { Enable(context.Context, *Arguments) (*Error, error) Disable(context.Context, *Arguments) (*Error, error) ModPath(Grpc_ModPathServer) error + GetPolicyPrefixes(*PolicyArguments, Grpc_GetPolicyPrefixesServer) error + GetPolicyPrefix(context.Context, *PolicyArguments) (*PrefixSet, error) + ModPolicyPrefix(Grpc_ModPolicyPrefixServer) error } func RegisterGrpcServer(s *grpc.Server, srv GrpcServer) { @@ -1293,6 +1445,65 @@ func (x *grpcModPathServer) Recv() (*ModPathArguments, error) { return m, nil } +func _Grpc_GetPolicyPrefixes_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(PolicyArguments) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(GrpcServer).GetPolicyPrefixes(m, &grpcGetPolicyPrefixesServer{stream}) +} + +type Grpc_GetPolicyPrefixesServer interface { + Send(*PrefixSet) error + grpc.ServerStream +} + +type grpcGetPolicyPrefixesServer struct { + grpc.ServerStream +} + +func (x *grpcGetPolicyPrefixesServer) Send(m *PrefixSet) error { + return x.ServerStream.SendMsg(m) +} + +func _Grpc_GetPolicyPrefix_Handler(srv interface{}, ctx context.Context, codec grpc.Codec, buf []byte) (interface{}, error) { + in := new(PolicyArguments) + if err := codec.Unmarshal(buf, in); err != nil { + return nil, err + } + out, err := srv.(GrpcServer).GetPolicyPrefix(ctx, in) + if err != nil { + return nil, err + } + return out, nil +} + +func _Grpc_ModPolicyPrefix_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(GrpcServer).ModPolicyPrefix(&grpcModPolicyPrefixServer{stream}) +} + +type Grpc_ModPolicyPrefixServer interface { + Send(*Error) error + Recv() (*PolicyArguments, error) + grpc.ServerStream +} + +type grpcModPolicyPrefixServer struct { + grpc.ServerStream +} + +func (x *grpcModPolicyPrefixServer) Send(m *Error) error { + return x.ServerStream.SendMsg(m) +} + +func (x *grpcModPolicyPrefixServer) Recv() (*PolicyArguments, error) { + m := new(PolicyArguments) + if err := x.ServerStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + var _Grpc_serviceDesc = grpc.ServiceDesc{ ServiceName: "api.Grpc", HandlerType: (*GrpcServer)(nil), @@ -1329,6 +1540,10 @@ var _Grpc_serviceDesc = grpc.ServiceDesc{ MethodName: "Disable", Handler: _Grpc_Disable_Handler, }, + { + MethodName: "GetPolicyPrefix", + Handler: _Grpc_GetPolicyPrefix_Handler, + }, }, Streams: []grpc.StreamDesc{ { @@ -1352,5 +1567,16 @@ var _Grpc_serviceDesc = grpc.ServiceDesc{ ServerStreams: true, ClientStreams: true, }, + { + StreamName: "GetPolicyPrefixes", + Handler: _Grpc_GetPolicyPrefixes_Handler, + ServerStreams: true, + }, + { + StreamName: "ModPolicyPrefix", + Handler: _Grpc_ModPolicyPrefix_Handler, + ServerStreams: true, + ClientStreams: true, + }, }, } diff --git a/api/gobgp.proto b/api/gobgp.proto index a3f4228d..6a2e3844 100644 --- a/api/gobgp.proto +++ b/api/gobgp.proto @@ -32,6 +32,9 @@ service Grpc { rpc Enable(Arguments) returns (Error) {} rpc Disable(Arguments) returns (Error) {} rpc ModPath(stream ModPathArguments) returns (stream Error) {} + rpc GetPolicyPrefixes(PolicyArguments) returns (stream PrefixSet) {} + rpc GetPolicyPrefix(PolicyArguments) returns (PrefixSet) {} + rpc ModPolicyPrefix(stream PolicyArguments) returns (stream Error) {} } message Error { @@ -54,11 +57,25 @@ message ModPathArguments { Path path = 2; } +message PolicyArguments { + Resource resource = 1; + Operation operation = 2; + string name = 3; + PrefixSet prefix_set = 4; +} + enum Resource { GLOBAL = 0; LOCAL = 1; ADJ_IN = 2; ADJ_OUT = 3; + POLICY_PREFIX = 4; +} + +enum Operation { + ADD = 0; + DEL = 1; + DEL_ALL = 2; } enum AFI { @@ -319,3 +336,14 @@ message Peer { PeerConf conf = 1; PeerInfo info = 2; } + +message Prefix { + string address = 1; + uint32 mask_length = 2; + string mask_length_range = 3; +} + +message PrefixSet { + string prefix_set_name = 1; + repeated Prefix prefix_list = 2; +} diff --git a/gobgp/main.go b/gobgp/main.go index 8fcd33fd..4f5a3052 100644 --- a/gobgp/main.go +++ b/gobgp/main.go @@ -35,6 +35,7 @@ import ( const ( CMD_GLOBAL = "global" CMD_NEIGHBOR = "neighbor" + CMD_POLICY = "policy" CMD_RIB = "rib" CMD_ADD = "add" CMD_DEL = "del" @@ -48,6 +49,8 @@ const ( CMD_SHUTDOWN = "shutdown" CMD_ENABLE = "enable" CMD_DISABLE = "disable" + CMD_PREFIX = "prefix" + CMD_ALL = "all" ) func formatTimedelta(d int64) string { @@ -134,6 +137,20 @@ func (p peers) Less(i, j int) bool { return strings.Less(0, 1) } +type prefixes []*api.PrefixSet + +func (p prefixes) Len() int { + return len(p) +} + +func (p prefixes) Swap(i, j int) { + p[i], p[j] = p[j], p[i] +} + +func (p prefixes) Less(i, j int) bool { + return p[i].PrefixSetName < p[j].PrefixSetName +} + func connGrpc() *grpc.ClientConn { timeout := grpc.WithTimeout(time.Second) @@ -166,9 +183,9 @@ func requestGrpc(cmd string, eArgs []string, remoteIP net.IP) error { return modPath(CMD_DEL, eArgs) case CMD_NEIGHBOR: if len(eArgs) == 0 { - showNeighbors() + return showNeighbors() } else { - showNeighbor(eArgs) + return showNeighbor(eArgs) } case CMD_NEIGHBOR + "_" + CMD_LOCAL: return showNeighborRib(api.Resource_LOCAL, remoteIP) @@ -190,6 +207,16 @@ func requestGrpc(cmd string, eArgs []string, remoteIP net.IP) error { return stateChangeNeighbor(CMD_ENABLE, remoteIP) case CMD_NEIGHBOR + "_" + CMD_DISABLE: return stateChangeNeighbor(CMD_DISABLE, remoteIP) + case CMD_POLICY + "_" + CMD_PREFIX: + if len(eArgs) == 0 { + return showPolicyPrefixes() + } else { + return showPolicyPrefix(eArgs) + } + case CMD_POLICY + "_" + CMD_PREFIX + "_" + CMD_ADD: + return modPolicyPrefix(CMD_ADD, eArgs) + case CMD_POLICY + "_" + CMD_PREFIX + "_" + CMD_DEL: + return modPolicyPrefix(CMD_DEL, eArgs) } return nil } @@ -252,29 +279,21 @@ func checkAddressFamily(ip net.IP) (*api.AddressFamily, error) { var client api.GrpcClient -type GlobalCommand struct { -} +type GlobalCommand struct{} func (x *GlobalCommand) Execute(args []string) error { eArgs := extractArgs(CMD_GLOBAL) parser := flags.NewParser(nil, flags.Default) parser.Usage = "global" - parser.AddCommand(CMD_RIB, "subcommand for rib of global", "", NewGlobalRibCommand(api.Resource_GLOBAL)) + parser.AddCommand(CMD_RIB, "subcommand for rib of global", "", &GlobalRibCommand{}) if _, err := parser.ParseArgs(eArgs); err != nil { os.Exit(1) } return nil } -type GlobalRibCommand struct { - resource api.Resource -} +type GlobalRibCommand struct{} -func NewGlobalRibCommand(resource api.Resource) *GlobalRibCommand { - return &GlobalRibCommand{ - resource: resource, - } -} func showGlobalRib() error { rt, err := checkAddressFamily(net.IP{}) if err != nil { @@ -334,8 +353,8 @@ func (x *GlobalRibCommand) Execute(args []string) error { } } else { parser.Usage = "global rib [OPTIONS]\n gobgp global rib" - parser.AddCommand(CMD_ADD, "subcommand for add route to global rib", "", NewGlobalRibAddCommand(x.resource)) - parser.AddCommand(CMD_DEL, "subcommand for delete route from global rib", "", NewGlobalRibDelCommand(x.resource)) + parser.AddCommand(CMD_ADD, "subcommand for add route to global rib", "", &GlobalRibAddCommand{}) + parser.AddCommand(CMD_DEL, "subcommand for delete route from global rib", "", &GlobalRibDelCommand{}) if _, err := parser.ParseArgs(eArgs); err != nil { os.Exit(1) } @@ -343,15 +362,7 @@ func (x *GlobalRibCommand) Execute(args []string) error { return nil } -type GlobalRibAddCommand struct { - resource api.Resource -} - -func NewGlobalRibAddCommand(resource api.Resource) *GlobalRibAddCommand { - return &GlobalRibAddCommand{ - resource: resource, - } -} +type GlobalRibAddCommand struct{} func modPath(modtype string, eArgs []string) error { rf, err := checkAddressFamily(net.IP{}) @@ -366,7 +377,7 @@ func modPath(modtype string, eArgs []string) error { if len(eArgs) == 1 || len(eArgs) == 3 { prefix = eArgs[0] } else { - return fmt.Errorf("usage: global rib add <prefix> -a { ipv4 | ipv6 }") + return fmt.Errorf("usage: global rib %s <prefix> -a { ipv4 | ipv6 }", modtype) } path.Nlri = &api.Nlri{ Af: rf, @@ -377,7 +388,7 @@ func modPath(modtype string, eArgs []string) error { macAddr = eArgs[0] ipAddr = eArgs[1] } else { - return fmt.Errorf("usage: global rib add <mac address> <ip address> -a evpn") + return fmt.Errorf("usage: global rib %s <mac address> <ip address> -a evpn", modtype) } path.Nlri = &api.Nlri{ Af: rf, @@ -391,7 +402,7 @@ func modPath(modtype string, eArgs []string) error { } case api.AF_ENCAP: if len(eArgs) < 3 { - return fmt.Errorf("usage: global rib add <end point ip address> [<vni>] -a encap") + return fmt.Errorf("usage: global rib %s <end point ip address> [<vni>] -a encap", modtype) } prefix = eArgs[0] @@ -498,15 +509,7 @@ func (x *GlobalRibAddCommand) Execute(args []string) error { return nil } -type GlobalRibDelCommand struct { - resource api.Resource -} - -func NewGlobalRibDelCommand(resource api.Resource) *GlobalRibDelCommand { - return &GlobalRibDelCommand{ - resource: resource, - } -} +type GlobalRibDelCommand struct{} func (x *GlobalRibDelCommand) Execute(args []string) error { eArgs := extractArgs(CMD_DEL) @@ -1081,6 +1084,309 @@ func (x *NeighborChangeStateCommand) Execute(args []string) error { return nil } +type PolicyCommand struct{} + +func (x *PolicyCommand) Execute(args []string) error { + eArgs := extractArgs(CMD_POLICY) + parser := flags.NewParser(nil, flags.Default) + parser.Usage = "policy" + parser.AddCommand(CMD_PREFIX, "subcommand for prefix of policy", "", &PolicyPrefixCommand{}) + if _, err := parser.ParseArgs(eArgs); err != nil { + os.Exit(1) + } + return nil +} + +type PolicyPrefixCommand struct{} + +func showPolicyPrefixes() error { + arg := &api.PolicyArguments{ + Resource: api.Resource_POLICY_PREFIX, + } + stream, e := client.GetPolicyPrefixes(context.Background(), arg) + if e != nil { + fmt.Println(e) + return e + } + m := prefixes{} + for { + p, e := stream.Recv() + if e == io.EOF { + break + } else if e != nil { + return e + } + m = append(m, p) + } + + if globalOpts.Json { + j, _ := json.Marshal(m) + fmt.Println(string(j)) + return nil + } + + if globalOpts.Quiet { + for _, p := range m { + fmt.Println(p.PrefixSetName) + } + return nil + } + maxnamelen := len("Name") + maxprefixlen := len("Prefix") + maxrangelen := len("MaskRange") + + sort.Sort(m) + + for _, ps := range m { + if len(ps.PrefixSetName) > maxnamelen { + maxnamelen = len(ps.PrefixSetName) + } + for _, p := range ps.PrefixList { + if len(p.Address)+len(fmt.Sprint(p.MaskLength)) > maxprefixlen { + maxprefixlen = len(p.Address) + len(fmt.Sprint(p.MaskLength)) + } + if len(p.MaskLengthRange) > maxrangelen { + maxrangelen = len(p.MaskLengthRange) + } + } + } + var format string + format = "%" + fmt.Sprint(maxnamelen) + "s %-" + fmt.Sprint(maxprefixlen) + "s %-" + fmt.Sprint(maxrangelen) + "s\n" + fmt.Printf(format, "Name", "Prefix", "MaskRange") + for _, ps := range m { + for i, p := range ps.PrefixList { + prefix := fmt.Sprintf("%s/%d", p.Address, p.MaskLength) + if i == 0 { + fmt.Printf(format, ps.PrefixSetName, prefix, p.MaskLengthRange) + } else { + fmt.Printf(format, "", prefix, p.MaskLengthRange) + } + } + } + return nil +} + +func showPolicyPrefix(args []string) error { + arg := &api.PolicyArguments{ + Resource: api.Resource_POLICY_PREFIX, + Name: args[0], + } + ps, e := client.GetPolicyPrefix(context.Background(), arg) + if e != nil { + return e + } + + if globalOpts.Json { + j, _ := json.Marshal(ps) + fmt.Println(string(j)) + return nil + } + + maxprefixlen := len("Prefix") + maxrangelen := len("MaskRange") + + for _, p := range ps.PrefixList { + if len(p.Address)+len(fmt.Sprint(p.MaskLength)) > maxprefixlen { + maxprefixlen = len(p.Address) + len(fmt.Sprint(p.MaskLength)) + } + if len(p.MaskLengthRange) > maxrangelen { + maxrangelen = len(p.MaskLengthRange) + } + } + var format string + format = "%-" + fmt.Sprint(maxprefixlen) + "s %-" + fmt.Sprint(maxrangelen) + "s\n" + fmt.Printf(format, "Prefix", "MaskRange") + + for _, p := range ps.PrefixList { + prefix := fmt.Sprintf("%s/%d", p.Address, p.MaskLength) + fmt.Printf(format, prefix, p.MaskLengthRange) + } + return nil +} + +func (x *PolicyPrefixCommand) Execute(args []string) error { + eArgs := extractArgs(CMD_PREFIX) + if len(eArgs) == 0 { + if err := requestGrpc(CMD_POLICY+"_"+CMD_PREFIX, eArgs, nil); err != nil { + return err + } + return nil + } else if len(eArgs) == 1 && !(eArgs[0] == "-h" || eArgs[0] == "--help" || eArgs[0] == "add" || eArgs[0] == "del") { + if err := requestGrpc(CMD_POLICY+"_"+CMD_PREFIX, eArgs, nil); err != nil { + return err + } + return nil + } + parser := flags.NewParser(nil, flags.Default) + parser.Usage = "policy prefix [OPTIONS]\n gobgp policy prefix" + parser.AddCommand(CMD_ADD, "subcommand for add route to policy prefix", "", &PolicyPrefixAddCommand{}) + parser.AddCommand(CMD_DEL, "subcommand for delete route from policy prefix", "", &PolicyPrefixDelCommand{}) + parser.ParseArgs(eArgs) + return nil +} + +type PolicyPrefixAddCommand struct{} + +func parsePrefixSet(eArgs []string) (*api.PrefixSet, error) { + _, ipNet, e := net.ParseCIDR(eArgs[1]) + if e != nil { + return nil, fmt.Errorf("prefix is invalid format %s\nplease enter ipv4 or ipv6 format", eArgs[1]) + } + mask, _ := ipNet.Mask.Size() + prefix := &api.Prefix{ + Address: ipNet.IP.String(), + MaskLength: uint32(mask), + } + + if len(eArgs) == 3 { + maskRange := eArgs[2] + idx := strings.Index(maskRange, "..") + if idx == -1 { + return nil, fmt.Errorf("mask length range invalid format %s", maskRange) + } + var min, max int + var e error + if idx != 0 { + if min, e = strconv.Atoi(maskRange[:idx]); e != nil { + return nil, fmt.Errorf("mask length range invalid format %s", maskRange) + } + } + if idx != len(maskRange)-1 { + if max, e = strconv.Atoi(maskRange[idx+2:]); e != nil { + return nil, fmt.Errorf("mask length range invalid format %s", maskRange) + } + } + if ipv4 := ipNet.IP.To4(); ipv4 != nil { + if min < 0 || 32 < max { + return nil, fmt.Errorf("ipv4 mask length range outside scope %s", maskRange) + } + } else { + if min < 0 || 128 < max { + return nil, fmt.Errorf("ipv6 mask length range outside scope %s", maskRange) + } + } + if min >= max { + return nil, fmt.Errorf("mask length range invalid format %s\nTo a large value to the right from the left", maskRange) + } + prefix.MaskLengthRange = maskRange + } + prefixList := []*api.Prefix{prefix} + prefixSet := &api.PrefixSet{ + PrefixSetName: eArgs[0], + PrefixList: prefixList, + } + return prefixSet, nil +} + +func modPolicyPrefix(modtype string, eArgs []string) error { + prefixSet := &api.PrefixSet{} + var e error + var operation api.Operation + + switch modtype { + case CMD_ADD: + if len(eArgs) < 2 { + return fmt.Errorf("policy prefix add <prefix name> <prefix> [<mask length renge>]") + } + if prefixSet, e = parsePrefixSet(eArgs); e != nil { + return e + } + operation = api.Operation_ADD + case CMD_DEL: + if len(eArgs) == 0 { + operation = api.Operation_DEL_ALL + } else if len(eArgs) == 1 { + prefixSet = &api.PrefixSet{ + PrefixSetName: eArgs[0], + PrefixList: nil, + } + operation = api.Operation_DEL + } else { + if prefixSet, e = parsePrefixSet(eArgs); e != nil { + return e + } + operation = api.Operation_DEL + } + } + + arg := &api.PolicyArguments{ + Resource: api.Resource_POLICY_PREFIX, + Operation: operation, + PrefixSet: prefixSet, + } + stream, err := client.ModPolicyPrefix(context.Background()) + if err != nil { + return err + } + err = stream.Send(arg) + if err != nil { + return err + } + stream.CloseSend() + + res, e := stream.Recv() + if e != nil { + return e + } + if res.Code != api.Error_SUCCESS { + return fmt.Errorf("error: code: %d, msg: %s", res.Code, res.Msg) + } + return nil +} + +func (x *PolicyPrefixAddCommand) Execute(args []string) error { + eArgs := extractArgs(CMD_ADD) + if len(eArgs) == 0 || len(eArgs) > 3 { + return fmt.Errorf("policy prefix add <prefix name> <prefix> [<mask length renge>]") + } else if !(eArgs[0] == "-h" || eArgs[0] == "--help") { + if err := requestGrpc(CMD_POLICY+"_"+CMD_PREFIX+"_"+CMD_ADD, eArgs, nil); err != nil { + return err + } + return nil + } + parser := flags.NewParser(nil, flags.Default) + parser.Usage = "policy prefix add <prefix name> <prefix> [mask length renge]" + parser.ParseArgs(eArgs) + return nil +} + +type PolicyPrefixDelCommand struct{} + +func (x *PolicyPrefixDelCommand) Execute(args []string) error { + eArgs := extractArgs(CMD_DEL) + if len(eArgs) > 3 { + return fmt.Errorf("policy prefix del [<prefix name> [<prefix> [<mask length range>]]] ") + } else if len(eArgs) > 0 && !(eArgs[0] == "-h" || eArgs[0] == "--help" || eArgs[0] == "all") { + if err := requestGrpc(CMD_POLICY+"_"+CMD_PREFIX+"_"+CMD_DEL, eArgs, nil); err != nil { + return err + } + return nil + } + parser := flags.NewParser(nil, flags.Default) + parser.Usage = "policy prefix del [ <prefix name> <prefix> ]\n policy prefix del" + parser.AddCommand(CMD_ALL, "subcommand for delete all route from policy prefix", "", &PolicyPrefixDelAllCommand{}) + parser.ParseArgs(eArgs) + return nil +} + +type PolicyPrefixDelAllCommand struct{} + +func (x *PolicyPrefixDelAllCommand) Execute(args []string) error { + eArgs := extractArgs(CMD_ALL) + if len(eArgs) > 0 && !(eArgs[0] == "-h" || eArgs[0] == "--help") { + return fmt.Errorf("Argument dose not input") + } else if len(eArgs) == 0 { + if err := requestGrpc(CMD_POLICY+"_"+CMD_PREFIX+"_"+CMD_DEL, eArgs, nil); err != nil { + return err + } + } + + parser := flags.NewParser(nil, flags.Default) + parser.Usage = "policy prefix del all" + parser.ParseArgs(eArgs) + return nil +} + var globalOpts struct { Host string `short:"u" long:"url" description:"specifying an url" default:"127.0.0.1"` Port int `short:"p" long:"port" description:"specifying a port" default:"8080"` @@ -1098,13 +1404,15 @@ var neighborsOpts struct { } func main() { - cmds = []string{CMD_GLOBAL, CMD_NEIGHBOR, CMD_RIB, CMD_ADD, CMD_DEL, CMD_LOCAL, CMD_ADJ_IN, CMD_ADJ_OUT, - CMD_RESET, CMD_SOFT_RESET, CMD_SOFT_RESET_IN, CMD_SOFT_RESET_OUT, CMD_SHUTDOWN, CMD_ENABLE, CMD_DISABLE} + cmds = []string{CMD_GLOBAL, CMD_NEIGHBOR, CMD_POLICY, CMD_RIB, CMD_ADD, CMD_DEL, CMD_LOCAL, CMD_ADJ_IN, CMD_ADJ_OUT, + CMD_RESET, CMD_SOFT_RESET, CMD_SOFT_RESET_IN, CMD_SOFT_RESET_OUT, CMD_SHUTDOWN, CMD_ENABLE, CMD_DISABLE, + CMD_PREFIX, CMD_ALL} eArgs := extractArgs("") parser := flags.NewParser(&globalOpts, flags.Default) parser.AddCommand(CMD_GLOBAL, "subcommand for global", "", &GlobalCommand{}) parser.AddCommand(CMD_NEIGHBOR, "subcommand for neighbor", "", &NeighborCommand{}) + parser.AddCommand(CMD_POLICY, "subcommand for policy", "", &PolicyCommand{}) if _, err := parser.ParseArgs(eArgs); err != nil { os.Exit(1) } diff --git a/server/grpc_server.go b/server/grpc_server.go index c218a4ac..cc0a0e16 100644 --- a/server/grpc_server.go +++ b/server/grpc_server.go @@ -43,6 +43,11 @@ const ( REQ_GLOBAL_RIB REQ_GLOBAL_ADD REQ_GLOBAL_DELETE + REQ_POLICY_PREFIX + REQ_POLICY_PREFIXES + REQ_POLICY_PREFIX_ADD + REQ_POLICY_PREFIX_DELETE + REQ_POLICY_PREFIXES_DELETE ) const GRPC_PORT = 8080 @@ -259,6 +264,116 @@ func (s *Server) ModPath(stream api.Grpc_ModPathServer) error { } } } +func (s *Server) getPolicies(reqType int, arg *api.PolicyArguments, stream interface{}) error { + var rf bgp.RouteFamily + req := NewGrpcRequest(reqType, "", rf, nil) + s.bgpServerCh <- req + for res := range req.ResponseCh { + if err := res.Err(); err != nil { + log.Debug(err.Error()) + return err + } + var err error + switch arg.Resource { + case api.Resource_POLICY_PREFIX: + err = stream.(api.Grpc_GetPolicyPrefixesServer).Send(res.Data.(*api.PrefixSet)) + default: + return fmt.Errorf("unsupported resource type: %v", arg.Resource) + } + if err != nil { + return err + } + } + return nil +} + +func (s *Server) getPolicy(arg *api.PolicyArguments) (interface{}, error) { + var rf bgp.RouteFamily + var reqType int + switch arg.Resource { + case api.Resource_POLICY_PREFIX: + reqType = REQ_POLICY_PREFIX + default: + return nil, fmt.Errorf("unsupported resource type: %v", arg.Resource) + } + req := NewGrpcRequest(reqType, "", rf, arg.Name) + s.bgpServerCh <- req + + res := <-req.ResponseCh + if err := res.Err(); err != nil { + log.Debug(err.Error()) + return nil, err + } + return res.Data, nil +} + +func (s *Server) modPolicy(arg *api.PolicyArguments, stream interface{}) error { + var rf bgp.RouteFamily + var reqType int + switch arg.Resource { + case api.Resource_POLICY_PREFIX: + switch arg.Operation { + case api.Operation_ADD: + reqType = REQ_POLICY_PREFIX_ADD + case api.Operation_DEL: + reqType = REQ_POLICY_PREFIX_DELETE + case api.Operation_DEL_ALL: + reqType = REQ_POLICY_PREFIXES_DELETE + default: + return fmt.Errorf("unsupported operation: %s", arg.Operation) + } + req := NewGrpcRequest(reqType, "", rf, arg.PrefixSet) + s.bgpServerCh <- req + + res := <-req.ResponseCh + if err := res.Err(); err != nil { + log.Debug(err.Error()) + return err + } + + err := stream.(api.Grpc_ModPolicyPrefixServer).Send(&api.Error{ + Code: api.Error_SUCCESS, + }) + + if err != nil { + return err + } + default: + return fmt.Errorf("unsupported resource type: %v", arg.Resource) + } + return nil + +} + +func (s *Server) GetPolicyPrefixes(arg *api.PolicyArguments, stream api.Grpc_GetPolicyPrefixesServer) error { + if err := s.getPolicies(REQ_POLICY_PREFIXES, arg, stream); err != nil { + return err + } + return nil +} + +func (s *Server) GetPolicyPrefix(ctx context.Context, arg *api.PolicyArguments) (*api.PrefixSet, error) { + data, err := s.getPolicy(arg) + if err != nil { + return nil, err + } + return data.(*api.PrefixSet), nil +} + +func (s *Server) ModPolicyPrefix(stream api.Grpc_ModPolicyPrefixServer) error { + for { + arg, err := stream.Recv() + if err == io.EOF { + return nil + } else if err != nil { + return err + } + if err := s.modPolicy(arg, stream); err != nil { + return err + } + return nil + } +} type GrpcRequest struct { RequestType int diff --git a/server/server.go b/server/server.go index 92b9b606..b2096d49 100644 --- a/server/server.go +++ b/server/server.go @@ -18,10 +18,12 @@ package server import ( "fmt" log "github.com/Sirupsen/logrus" + "github.com/osrg/gobgp/api" "github.com/osrg/gobgp/config" "github.com/osrg/gobgp/policy" "net" "os" + "reflect" "strconv" ) @@ -65,6 +67,7 @@ type BgpServer struct { globalRib *Peer policyUpdateCh chan config.RoutingPolicy policyMap map[string]*policy.Policy + routingPolicy config.RoutingPolicy } func NewBgpServer(port int) *BgpServer { @@ -219,12 +222,7 @@ func (server *BgpServer) Serve() { case grpcReq := <-server.GrpcReqCh: server.handleGrpc(grpcReq) case pl := <-server.policyUpdateCh: - server.SetPolicy(pl) - msg := &serverMsg{ - msgType: SRV_MSG_POLICY_UPDATED, - msgData: server.policyMap, - } - sendServerMsgToAll(server.peerMap, msg) + server.handlePolicy(pl) } } } @@ -266,6 +264,16 @@ func (server *BgpServer) SetPolicy(pl config.RoutingPolicy) { pMap[p.Name] = policy.NewPolicy(p.Name, p, df) } server.policyMap = pMap + server.routingPolicy = pl +} + +func (server *BgpServer) handlePolicy(pl config.RoutingPolicy) { + server.SetPolicy(pl) + msg := &serverMsg{ + msgType: SRV_MSG_POLICY_UPDATED, + msgData: server.policyMap, + } + sendServerMsgToAll(server.peerMap, msg) } func (server *BgpServer) handleGrpc(grpcReq *GrpcRequest) { @@ -318,5 +326,169 @@ func (server *BgpServer) handleGrpc(grpcReq *GrpcRequest) { grpcReq.ResponseCh <- result close(grpcReq.ResponseCh) } + case REQ_POLICY_PREFIXES: + info := server.routingPolicy.DefinedSets.PrefixSetList + result := &GrpcResponse{} + if len(info) > 0 { + for _, ps := range info { + resPrefixSet := prefixToApiStruct(ps) + result = &GrpcResponse{ + Data: resPrefixSet, + } + grpcReq.ResponseCh <- result + } + } else { + result.ResponseErr = fmt.Errorf("Policy Prefix is not exist.") + grpcReq.ResponseCh <- result + } + close(grpcReq.ResponseCh) + case REQ_POLICY_PREFIX: + name := grpcReq.Data.(string) + info := server.routingPolicy.DefinedSets.PrefixSetList + result := &GrpcResponse{} + resPrefixSet := &api.PrefixSet{} + for _, ps := range info { + if ps.PrefixSetName == name { + resPrefixSet = prefixToApiStruct(ps) + break + } + } + if len(resPrefixSet.PrefixList) > 0 { + result = &GrpcResponse{ + Data: resPrefixSet, + } + grpcReq.ResponseCh <- result + } else { + result.ResponseErr = fmt.Errorf("Policy Prefix that has %v does not exist.", name) + grpcReq.ResponseCh <- result + } + close(grpcReq.ResponseCh) + case REQ_POLICY_PREFIX_ADD: + reqPrefixSet := grpcReq.Data.(*api.PrefixSet) + conPrefixSetList := server.routingPolicy.DefinedSets.PrefixSetList + result := &GrpcResponse{} + isReqPrefixSet, prefixSet := prefixToConfigStruct(reqPrefixSet) + if !isReqPrefixSet { + result.ResponseErr = fmt.Errorf("dose not reqest of policy prefix.") + grpcReq.ResponseCh <- result + close(grpcReq.ResponseCh) + } + idxPrefixSet, idxPrefix := findPrefixSet(conPrefixSetList, reqPrefixSet, prefixSet) + if idxPrefixSet == -1 { + conPrefixSetList = append(conPrefixSetList, prefixSet) + } else { + if idxPrefix == -1 { + conPrefixSetList[idxPrefixSet].PrefixList = append(conPrefixSetList[idxPrefixSet].PrefixList, prefixSet.PrefixList[0]) + } + } + server.routingPolicy.DefinedSets.PrefixSetList = conPrefixSetList + server.handlePolicy(server.routingPolicy) + grpcReq.ResponseCh <- result + close(grpcReq.ResponseCh) + case REQ_POLICY_PREFIX_DELETE: + reqPrefixSet := grpcReq.Data.(*api.PrefixSet) + conPrefixSetList := server.routingPolicy.DefinedSets.PrefixSetList + result := &GrpcResponse{} + isReqPrefixSet, prefixSet := prefixToConfigStruct(reqPrefixSet) + if isReqPrefixSet { + idxPrefixSet, idxPrefix := findPrefixSet(conPrefixSetList, reqPrefixSet, prefixSet) + if idxPrefixSet == -1 { + result.ResponseErr = fmt.Errorf("Policy Prefix %v %v/%v %v does not exist.", prefixSet.PrefixSetName, + prefixSet.PrefixList[0].Address, prefixSet.PrefixList[0].Masklength, prefixSet.PrefixList[0].MasklengthRange) + } else { + if idxPrefix == -1 { + result.ResponseErr = fmt.Errorf("Policy Prefix %v %v/%v %v does not exist.", prefixSet.PrefixSetName, + prefixSet.PrefixList[0].Address, prefixSet.PrefixList[0].Masklength, prefixSet.PrefixList[0].MasklengthRange) + } else { + copy(conPrefixSetList[idxPrefixSet].PrefixList[idxPrefix:], conPrefixSetList[idxPrefixSet].PrefixList[idxPrefix+1:]) + conPrefixSetList[idxPrefixSet].PrefixList = conPrefixSetList[idxPrefixSet].PrefixList[:len(conPrefixSetList[idxPrefixSet].PrefixList)-1] + } + } + } else { + idxPrefixSet := -1 + for i, conPrefixSet := range conPrefixSetList { + if conPrefixSet.PrefixSetName == reqPrefixSet.PrefixSetName { + idxPrefixSet = i + break + } + } + if idxPrefixSet == -1 { + result.ResponseErr = fmt.Errorf("Policy Prefix %v does not exist.", prefixSet.PrefixSetName) + } else { + copy(conPrefixSetList[idxPrefixSet:], conPrefixSetList[idxPrefixSet+1:]) + conPrefixSetList = conPrefixSetList[:len(conPrefixSetList)-1] + } + } + server.routingPolicy.DefinedSets.PrefixSetList = conPrefixSetList + server.handlePolicy(server.routingPolicy) + grpcReq.ResponseCh <- result + close(grpcReq.ResponseCh) + case REQ_POLICY_PREFIXES_DELETE: + result := &GrpcResponse{} + pl := config.RoutingPolicy{} + server.handlePolicy(pl) + grpcReq.ResponseCh <- result + close(grpcReq.ResponseCh) + } +} + +func findPrefixSet(conPrefixSetList []config.PrefixSet, reqPrefixSet *api.PrefixSet, prefixSet config.PrefixSet) (int, int) { + idxPrefixSet := -1 + idxPrefix := -1 + for i, conPrefixSet := range conPrefixSetList { + if conPrefixSet.PrefixSetName == reqPrefixSet.PrefixSetName { + idxPrefixSet = i + for j, conPrefix := range conPrefixSet.PrefixList { + if reflect.DeepEqual(conPrefix.Address, prefixSet.PrefixList[0].Address) && conPrefix.Masklength == prefixSet.PrefixList[0].Masklength && + conPrefix.MasklengthRange == prefixSet.PrefixList[0].MasklengthRange { + idxPrefix = j + break + } + } + } + } + return idxPrefixSet, idxPrefix +} + +func prefixToApiStruct(ps config.PrefixSet) *api.PrefixSet { + resPrefixList := make([]*api.Prefix, 0) + for _, p := range ps.PrefixList { + resPrefix := &api.Prefix{ + Address: p.Address.String(), + MaskLength: uint32(p.Masklength), + MaskLengthRange: p.MasklengthRange, + } + resPrefixList = append(resPrefixList, resPrefix) + } + resPrefixSet := &api.PrefixSet{ + PrefixSetName: ps.PrefixSetName, + PrefixList: resPrefixList, + } + return resPrefixSet +} + +func prefixToConfigStruct(reqPrefixSet *api.PrefixSet) (bool, config.PrefixSet) { + var prefix config.Prefix + var prefixSet config.PrefixSet + isReqPrefixSet := true + if reqPrefixSet.PrefixList != nil { + prefix = config.Prefix{ + Address: net.ParseIP(reqPrefixSet.PrefixList[0].Address), + Masklength: uint8(reqPrefixSet.PrefixList[0].MaskLength), + MasklengthRange: reqPrefixSet.PrefixList[0].MaskLengthRange, + } + prefixList := []config.Prefix{prefix} + + prefixSet = config.PrefixSet{ + PrefixSetName: reqPrefixSet.PrefixSetName, + PrefixList: prefixList, + } + } else { + isReqPrefixSet = false + prefixSet = config.PrefixSet{ + PrefixSetName: reqPrefixSet.PrefixSetName, + PrefixList: nil, + } } + return isReqPrefixSet, prefixSet } |