diff options
-rw-r--r-- | api/gobgp.pb.go | 44 | ||||
-rw-r--r-- | api/gobgp.proto | 6 | ||||
-rw-r--r-- | server/grpc_server.go | 5 | ||||
-rw-r--r-- | server/server.go | 36 | ||||
-rw-r--r-- | table/policy.go | 206 |
5 files changed, 296 insertions, 1 deletions
diff --git a/api/gobgp.pb.go b/api/gobgp.pb.go index d3bdc1e4..e3d4e8b3 100644 --- a/api/gobgp.pb.go +++ b/api/gobgp.pb.go @@ -16,6 +16,7 @@ It has these top-level messages: MrtArguments ModVrfArguments ModDefinedSetArguments + ModStatementArguments Path Destination PeerConf @@ -302,6 +303,22 @@ func (m *ModDefinedSetArguments) GetSet() *DefinedSet { return nil } +type ModStatementArguments struct { + Operation Operation `protobuf:"varint,1,opt,name=operation,enum=gobgpapi.Operation" json:"operation,omitempty"` + Statement *Statement `protobuf:"bytes,2,opt,name=statement" json:"statement,omitempty"` +} + +func (m *ModStatementArguments) Reset() { *m = ModStatementArguments{} } +func (m *ModStatementArguments) String() string { return proto.CompactTextString(m) } +func (*ModStatementArguments) ProtoMessage() {} + +func (m *ModStatementArguments) GetStatement() *Statement { + if m != nil { + return m.Statement + } + return nil +} + type Path struct { Nlri []byte `protobuf:"bytes,1,opt,name=nlri,proto3" json:"nlri,omitempty"` Pattrs [][]byte `protobuf:"bytes,2,rep,name=pattrs,proto3" json:"pattrs,omitempty"` @@ -751,6 +768,7 @@ type GobgpApiClient interface { ModDefinedSet(ctx context.Context, in *ModDefinedSetArguments, opts ...grpc.CallOption) (*Error, error) GetStatement(ctx context.Context, in *Statement, opts ...grpc.CallOption) (*Statement, error) GetStatements(ctx context.Context, in *Statement, opts ...grpc.CallOption) (GobgpApi_GetStatementsClient, error) + ModStatement(ctx context.Context, in *ModStatementArguments, opts ...grpc.CallOption) (*Error, error) } type gobgpApiClient struct { @@ -1335,6 +1353,15 @@ func (x *gobgpApiGetStatementsClient) Recv() (*Statement, error) { return m, nil } +func (c *gobgpApiClient) ModStatement(ctx context.Context, in *ModStatementArguments, opts ...grpc.CallOption) (*Error, error) { + out := new(Error) + err := grpc.Invoke(ctx, "/gobgpapi.GobgpApi/ModStatement", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // Server API for GobgpApi service type GobgpApiServer interface { @@ -1366,6 +1393,7 @@ type GobgpApiServer interface { ModDefinedSet(context.Context, *ModDefinedSetArguments) (*Error, error) GetStatement(context.Context, *Statement) (*Statement, error) GetStatements(*Statement, GobgpApi_GetStatementsServer) error + ModStatement(context.Context, *ModStatementArguments) (*Error, error) } func RegisterGobgpApiServer(s *grpc.Server, srv GobgpApiServer) { @@ -1849,6 +1877,18 @@ func (x *gobgpApiGetStatementsServer) Send(m *Statement) error { return x.ServerStream.SendMsg(m) } +func _GobgpApi_ModStatement_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) { + in := new(ModStatementArguments) + if err := dec(in); err != nil { + return nil, err + } + out, err := srv.(GobgpApiServer).ModStatement(ctx, in) + if err != nil { + return nil, err + } + return out, nil +} + var _GobgpApi_serviceDesc = grpc.ServiceDesc{ ServiceName: "gobgpapi.GobgpApi", HandlerType: (*GobgpApiServer)(nil), @@ -1909,6 +1949,10 @@ var _GobgpApi_serviceDesc = grpc.ServiceDesc{ MethodName: "GetStatement", Handler: _GobgpApi_GetStatement_Handler, }, + { + MethodName: "ModStatement", + Handler: _GobgpApi_ModStatement_Handler, + }, }, Streams: []grpc.StreamDesc{ { diff --git a/api/gobgp.proto b/api/gobgp.proto index ad070e60..84eb8c74 100644 --- a/api/gobgp.proto +++ b/api/gobgp.proto @@ -48,6 +48,7 @@ service GobgpApi { rpc ModDefinedSet(ModDefinedSetArguments) returns (Error) {} rpc GetStatement(Statement) returns (Statement) {} rpc GetStatements(Statement) returns (stream Statement) {} + rpc ModStatement(ModStatementArguments) returns (Error) {} } message Error { @@ -97,6 +98,11 @@ message ModDefinedSetArguments { DefinedSet set = 2; } +message ModStatementArguments { + Operation operation = 1; + Statement statement = 2; +} + enum Resource { GLOBAL = 0; LOCAL = 1; diff --git a/server/grpc_server.go b/server/grpc_server.go index 6753417b..d68ecd96 100644 --- a/server/grpc_server.go +++ b/server/grpc_server.go @@ -62,6 +62,7 @@ const ( REQ_DEFINED_SET REQ_MOD_DEFINED_SET REQ_STATEMENT + REQ_MOD_STATEMENT ) const GRPC_PORT = 8080 @@ -481,6 +482,10 @@ func (s *Server) GetStatements(arg *api.Statement, stream api.GobgpApi_GetStatem }) } +func (s *Server) ModStatement(ctx context.Context, arg *api.ModStatementArguments) (*api.Error, error) { + return s.mod(REQ_MOD_STATEMENT, arg) +} + type GrpcRequest struct { RequestType int Name string diff --git a/server/server.go b/server/server.go index e25d314f..69c27f14 100644 --- a/server/server.go +++ b/server/server.go @@ -1650,6 +1650,12 @@ func (server *BgpServer) handleGrpc(grpcReq *GrpcRequest) []*SenderMsg { } } close(grpcReq.ResponseCh) + case REQ_MOD_STATEMENT: + err := server.handleGrpcModStatement(grpcReq) + grpcReq.ResponseCh <- &GrpcResponse{ + ResponseErr: err, + } + close(grpcReq.ResponseCh) case REQ_POLICY_ROUTEPOLICY, REQ_POLICY_ROUTEPOLICIES: info := server.policy.PolicyMap typ := grpcReq.RequestType @@ -1783,6 +1789,36 @@ func (server *BgpServer) handleGrpcGetStatement(grpcReq *GrpcRequest) error { return nil } +func (server *BgpServer) handleGrpcModStatement(grpcReq *GrpcRequest) error { + arg := grpcReq.Data.(*api.ModStatementArguments) + s, err := table.NewStatementFromApiStruct(arg.Statement, server.policy.DefinedSetMap) + if err != nil { + return err + } + m := server.policy.StatementMap + name := s.Name + d, ok := m[name] + switch arg.Operation { + case api.Operation_ADD: + if ok { + err = d.Add(s) + } else { + m[name] = s + } + case api.Operation_DEL: + err = d.Remove(s) + case api.Operation_DEL_ALL: + if server.policy.StatementInUse(d) { + return fmt.Errorf("can't delete. statement %s is in use", name) + } + delete(m, name) + case api.Operation_REPLACE: + err = d.Replace(s) + } + return err + +} + func (server *BgpServer) handleMrt(grpcReq *GrpcRequest) { now := uint32(time.Now().Unix()) view := "" diff --git a/table/policy.go b/table/policy.go index 8375faab..1d8cb161 100644 --- a/table/policy.go +++ b/table/policy.go @@ -96,6 +96,28 @@ var CommunityOptionValueMap = map[string]config.BgpSetCommunityOptionType{ CommunityOptionNameMap[config.BGP_SET_COMMUNITY_OPTION_TYPE_REPLACE]: config.BGP_SET_COMMUNITY_OPTION_TYPE_REPLACE, } +type ConditionType int + +const ( + CONDITION_PREFIX ConditionType = iota + CONDITION_NEIGHBOR + CONDITION_AS_PATH + CONDITION_COMMUNITY + CONDITION_EXT_COMMUNITY + CONDITION_AS_PATH_LENGTH + CONDITION_RPKI +) + +type ActionType int + +const ( + ACTION_ROUTING ActionType = iota + ACTION_COMMUNITY + ACTION_EXT_COMMUNITY + ACTION_MED + ACTION_AS_PATH_PREPEND +) + func NewMatchOption(c interface{}) (MatchOption, error) { switch c.(type) { case config.MatchSetOptionsType: @@ -779,6 +801,7 @@ func NewDefinedSetFromApiStruct(a *api.DefinedSet) (DefinedSet, error) { } type Condition interface { + Type() ConditionType Evaluate(*Path) bool Set() DefinedSet } @@ -788,6 +811,10 @@ type PrefixCondition struct { option MatchOption } +func (c *PrefixCondition) Type() ConditionType { + return CONDITION_PREFIX +} + func (c *PrefixCondition) Set() DefinedSet { return c.set } @@ -869,6 +896,10 @@ type NeighborCondition struct { option MatchOption } +func (c *NeighborCondition) Type() ConditionType { + return CONDITION_NEIGHBOR +} + func (c *NeighborCondition) Set() DefinedSet { return c.set } @@ -954,6 +985,10 @@ type AsPathCondition struct { option MatchOption } +func (c *AsPathCondition) Type() ConditionType { + return CONDITION_AS_PATH +} + func (c *AsPathCondition) Set() DefinedSet { return c.set } @@ -1031,6 +1066,10 @@ type CommunityCondition struct { option MatchOption } +func (c *CommunityCondition) Type() ConditionType { + return CONDITION_COMMUNITY +} + func (c *CommunityCondition) Set() DefinedSet { return c.set } @@ -1111,6 +1150,10 @@ type ExtCommunityCondition struct { option MatchOption } +func (c *ExtCommunityCondition) Type() ConditionType { + return CONDITION_EXT_COMMUNITY +} + func (c *ExtCommunityCondition) Set() DefinedSet { return c.set } @@ -1197,6 +1240,10 @@ type AsPathLengthCondition struct { operator AttributeComparison } +func (c *AsPathLengthCondition) Type() ConditionType { + return CONDITION_AS_PATH_LENGTH +} + // compare AS_PATH length in the message's AS_PATH attribute with // the one in condition. func (c *AsPathLengthCondition) Evaluate(path *Path) bool { @@ -1265,6 +1312,10 @@ type RpkiValidationCondition struct { result config.RpkiValidationResultType } +func (c *RpkiValidationCondition) Type() ConditionType { + return CONDITION_RPKI +} + func (c *RpkiValidationCondition) Evaluate(path *Path) bool { return c.result == path.Validation } @@ -1288,6 +1339,7 @@ func NewRpkiValidationCondition(c config.RpkiValidationResultType) (*RpkiValidat } type Action interface { + Type() ActionType Apply(*Path) *Path } @@ -1295,6 +1347,10 @@ type RoutingAction struct { AcceptRoute bool } +func (a *RoutingAction) Type() ActionType { + return ACTION_ROUTING +} + func (a *RoutingAction) Apply(path *Path) *Path { if a.AcceptRoute { return path @@ -1384,6 +1440,10 @@ func RegexpRemoveExtCommunities(path *Path, exps []*regexp.Regexp, subtypes []bg path.SetExtCommunities(newComms, true) } +func (a *CommunityAction) Type() ActionType { + return ACTION_COMMUNITY +} + func (a *CommunityAction) Apply(path *Path) *Path { switch a.action { case config.BGP_SET_COMMUNITY_OPTION_TYPE_ADD: @@ -1492,6 +1552,10 @@ type ExtCommunityAction struct { subtypeList []bgp.ExtendedCommunityAttrSubType } +func (a *ExtCommunityAction) Type() ActionType { + return ACTION_EXT_COMMUNITY +} + func (a *ExtCommunityAction) Apply(path *Path) *Path { switch a.action { case config.BGP_SET_COMMUNITY_OPTION_TYPE_ADD: @@ -1611,6 +1675,10 @@ type MedAction struct { action MedActionType } +func (a *MedAction) Type() ActionType { + return ACTION_MED +} + func (a *MedAction) Apply(path *Path) *Path { var err error switch a.action { @@ -1678,6 +1746,10 @@ type AsPathPrependAction struct { repeat uint8 } +func (a *AsPathPrependAction) Type() ActionType { + return ACTION_AS_PATH_PREPEND +} + func (a *AsPathPrependAction) Apply(path *Path) *Path { var asn uint32 if a.useLeftMost { @@ -1779,6 +1851,14 @@ func (s *Statement) Apply(path *Path) (RouteType, *Path) { }).Debug("statement evaluate : ", result) if result { //Routing action + if s.RouteAction == nil { + log.WithFields(log.Fields{ + "Topic": "Policy", + "Path": path, + "PolicyName": s.Name, + }).Warn("route action is nil") + return ROUTE_TYPE_REJECT, path + } p := s.RouteAction.Apply(path) if p == nil { return ROUTE_TYPE_REJECT, path @@ -1837,7 +1917,120 @@ func (s *Statement) ToApiStruct() *api.Statement { } } -func NewStatementFromApiStruct(a api.Statement, dmap DefinedSetMap) (*Statement, error) { +type opType int + +const ( + ADD opType = iota + REMOVE + REPLACE +) + +func (lhs *Statement) mod(op opType, rhs *Statement) error { + cs := make([]Condition, len(lhs.Conditions)) + copy(cs, lhs.Conditions) + ra := lhs.RouteAction + as := make([]Action, len(lhs.ModActions)) + copy(as, lhs.ModActions) + for _, x := range rhs.Conditions { + var c Condition + i := 0 + for idx, y := range lhs.Conditions { + if x.Type() == y.Type() { + c = y + i = idx + break + } + } + switch op { + case ADD: + if c != nil { + return fmt.Errorf("condition %d is already set", c.Type()) + } + if cs == nil { + cs = make([]Condition, len(rhs.Conditions)) + } + cs = append(cs, x) + case REMOVE: + if c == nil { + return fmt.Errorf("condition %d is not set", c.Type()) + } + cs = append(cs[:i], cs[i+1:]...) + case REPLACE: + if c == nil { + return fmt.Errorf("condition %d is not set", c.Type()) + } + cs[i] = x + } + } + if rhs.RouteAction != nil { + switch op { + case ADD: + if lhs.RouteAction != nil { + return fmt.Errorf("route action is already set") + } + ra = rhs.RouteAction + case REMOVE: + if lhs.RouteAction == nil { + return fmt.Errorf("route action is not set") + } + ra = nil + case REPLACE: + if lhs.RouteAction == nil { + return fmt.Errorf("route action is not set") + } + ra = rhs.RouteAction + } + } + for _, x := range rhs.ModActions { + var a Action + i := 0 + for idx, y := range lhs.ModActions { + if x.Type() == y.Type() { + a = y + i = idx + break + } + } + switch op { + case ADD: + if a != nil { + return fmt.Errorf("action %d is already set", a.Type()) + } + if as == nil { + as = make([]Action, len(rhs.ModActions)) + } + as = append(as, a) + case REMOVE: + if a == nil { + return fmt.Errorf("action %d is not set", a.Type()) + } + as = append(as[:i], as[i+1:]...) + case REPLACE: + if a == nil { + return fmt.Errorf("action %d is not set", a.Type()) + } + as[i] = x + } + } + lhs.Conditions = cs + lhs.RouteAction = ra + lhs.ModActions = as + return nil +} + +func (lhs *Statement) Add(rhs *Statement) error { + return lhs.mod(ADD, rhs) +} + +func (lhs *Statement) Remove(rhs *Statement) error { + return lhs.mod(REMOVE, rhs) +} + +func (lhs *Statement) Replace(rhs *Statement) error { + return lhs.mod(REPLACE, rhs) +} + +func NewStatementFromApiStruct(a *api.Statement, dmap DefinedSetMap) (*Statement, error) { if a.Name == "" { return nil, fmt.Errorf("empty statement name") } @@ -2070,6 +2263,17 @@ func (r *RoutingPolicy) InUse(d DefinedSet) bool { return false } +func (r *RoutingPolicy) StatementInUse(x *Statement) bool { + for _, p := range r.PolicyMap { + for _, y := range p.Statements { + if x.Name == y.Name { + return true + } + } + } + return false +} + func NewRoutingPolicy(c config.RoutingPolicy) (*RoutingPolicy, error) { dmap := make(map[DefinedType]map[string]DefinedSet) dmap[DEFINED_TYPE_PREFIX] = make(map[string]DefinedSet) |