github.com/zhyoulun/cilium@v1.6.12/proxylib/memcached/parser.go (about)

     1  // Copyright 2018 Authors of Cilium
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // text memcache protocol parser based on https://github.com/memcached/memcached/blob/master/doc/protocol.txt
    16  
    17  package memcache
    18  
    19  import (
    20  	"bytes"
    21  	"fmt"
    22  	"regexp"
    23  
    24  	"github.com/cilium/cilium/proxylib/proxylib"
    25  
    26  	"github.com/cilium/cilium/proxylib/memcached/binary"
    27  	"github.com/cilium/cilium/proxylib/memcached/meta"
    28  	"github.com/cilium/cilium/proxylib/memcached/text"
    29  
    30  	"github.com/cilium/proxy/go/cilium/api"
    31  	log "github.com/sirupsen/logrus"
    32  )
    33  
    34  // Rule matches against memcached requests
    35  type Rule struct {
    36  	// allowed commands
    37  	commands memcacheCommandSet
    38  	// compiled regex
    39  	keyExact  []byte
    40  	keyPrefix []byte
    41  	regex     *regexp.Regexp
    42  
    43  	empty bool
    44  }
    45  
    46  // Matches returns true if the Rule matches
    47  func (rule *Rule) Matches(data interface{}) bool {
    48  	log.Debugf("memcache checking rule %v", *rule)
    49  
    50  	packetMeta, ok := data.(meta.MemcacheMeta)
    51  
    52  	if !ok {
    53  		log.Debugf("Wrong type supplied to Rule.Matches")
    54  		return false
    55  	}
    56  
    57  	if rule.empty {
    58  		return true
    59  	}
    60  
    61  	if packetMeta.IsBinary() {
    62  		if !rule.matchOpcode(packetMeta.Opcode) {
    63  			return false
    64  		}
    65  	} else {
    66  		if !rule.matchCommand(packetMeta.Command) {
    67  			return false
    68  		}
    69  	}
    70  
    71  	if len(rule.keyExact) > 0 {
    72  		for _, key := range packetMeta.Keys {
    73  			if !bytes.Equal(rule.keyExact, key) {
    74  				return false
    75  			}
    76  		}
    77  		return true
    78  	}
    79  
    80  	if len(rule.keyPrefix) > 0 {
    81  		for _, key := range packetMeta.Keys {
    82  			if !bytes.HasPrefix(key, rule.keyPrefix) {
    83  				return false
    84  			}
    85  		}
    86  		return true
    87  	}
    88  
    89  	if rule.regex != nil {
    90  		for _, key := range packetMeta.Keys {
    91  			if !rule.regex.Match(key) {
    92  				return false
    93  			}
    94  		}
    95  		return true
    96  	}
    97  
    98  	log.Debugf("No key rule specified, accepted by command match")
    99  	return true
   100  }
   101  
   102  func (rule *Rule) matchCommand(cmd string) bool {
   103  	_, ok := rule.commands.text[cmd]
   104  	return ok
   105  }
   106  
   107  func (rule *Rule) matchOpcode(code byte) bool {
   108  	_, ok := rule.commands.binary[code]
   109  	return ok
   110  }
   111  
   112  // L7RuleParser parses protobuf L7 rules to and array of Rule
   113  // May panic
   114  func L7RuleParser(rule *cilium.PortNetworkPolicyRule) []proxylib.L7NetworkPolicyRule {
   115  	var rules []proxylib.L7NetworkPolicyRule
   116  	l7Rules := rule.GetL7Rules()
   117  	if l7Rules == nil {
   118  		return rules
   119  	}
   120  	for _, l7Rule := range l7Rules.GetL7Rules() {
   121  		var br Rule
   122  		var commandFound = false
   123  		for k, v := range l7Rule.Rule {
   124  			switch k {
   125  			case "command":
   126  				br.commands, commandFound = MemcacheOpCodeMap[v]
   127  			case "keyExact":
   128  				br.keyExact = []byte(v)
   129  			case "keyPrefix":
   130  				br.keyPrefix = []byte(v)
   131  			case "keyRegex":
   132  				br.regex = regexp.MustCompile(v)
   133  			default:
   134  				proxylib.ParseError(fmt.Sprintf("Unsupported key: %s", k), rule)
   135  			}
   136  		}
   137  		if !commandFound {
   138  			if len(br.keyExact) > 0 || len(br.keyPrefix) > 0 || br.regex != nil {
   139  				proxylib.ParseError("command not specified but key was provided", rule)
   140  			} else {
   141  				br.empty = true
   142  			}
   143  		}
   144  		log.Debugf("Parsed Rule pair: %v", br)
   145  		rules = append(rules, &br)
   146  	}
   147  	return rules
   148  }
   149  
   150  // ParserFactory implements proxylib.ParserFactory
   151  type ParserFactory struct{}
   152  
   153  // Create creates memcached parser
   154  func (p *ParserFactory) Create(connection *proxylib.Connection) proxylib.Parser {
   155  	log.Debugf("ParserFactory: Create: %v", connection)
   156  	return &Parser{
   157  		connection: connection,
   158  	}
   159  }
   160  
   161  // compile time check for interface implementation
   162  var _ proxylib.ParserFactory = &ParserFactory{}
   163  
   164  var memcacheParserFactory *ParserFactory
   165  
   166  const (
   167  	parserName = "memcache"
   168  )
   169  
   170  func init() {
   171  	log.Debug("init(): Registering memcacheParserFactory")
   172  	proxylib.RegisterParserFactory(parserName, memcacheParserFactory)
   173  	proxylib.RegisterL7RuleParser(parserName, L7RuleParser)
   174  }
   175  
   176  // Parser implements proxylib.Parser
   177  type Parser struct {
   178  	connection *proxylib.Connection
   179  	parser     proxylib.Parser
   180  	isBinary   *bool
   181  }
   182  
   183  var _ proxylib.Parser = &Parser{}
   184  
   185  // OnData parses memcached data
   186  func (p *Parser) OnData(reply, endStream bool, dataBuffers [][]byte) (proxylib.OpType, int) {
   187  	if p.parser == nil {
   188  		var magicByte byte
   189  		if len(dataBuffers) > 0 && len(dataBuffers[0]) > 0 {
   190  			magicByte = dataBuffers[0][0]
   191  		} else {
   192  			return proxylib.NOP, 0
   193  		}
   194  
   195  		if magicByte >= 128 {
   196  			p.parser = binary.ParserFactoryInstance.Create(p.connection)
   197  		} else {
   198  			p.parser = text.ParserFactoryInstance.Create(p.connection)
   199  		}
   200  	}
   201  	return p.parser.OnData(reply, endStream, dataBuffers)
   202  }
   203  
   204  type memcacheCommandSet struct {
   205  	text   map[string]struct{}
   206  	binary map[byte]struct{}
   207  }
   208  
   209  // empty var for filling map below which will be used as set
   210  var e = struct{}{}
   211  
   212  // MemcacheOpCodeMap maps operation names and groups used in policy rules to sets of operation names and opcodes that are allowed in such a policy rule.
   213  // for more information on protocol check https://github.com/memcached/memcached/wiki/Protocols
   214  var MemcacheOpCodeMap = map[string]memcacheCommandSet{
   215  	"add": {
   216  		text:   map[string]struct{}{"add": e},
   217  		binary: map[byte]struct{}{2: e, 18: e},
   218  	},
   219  	"set": {
   220  		text:   map[string]struct{}{"set": e},
   221  		binary: map[byte]struct{}{1: e, 17: e},
   222  	},
   223  	"replace": {
   224  		text:   map[string]struct{}{"replace": e},
   225  		binary: map[byte]struct{}{3: e, 19: e},
   226  	},
   227  	"append": {
   228  		text:   map[string]struct{}{"append": e},
   229  		binary: map[byte]struct{}{14: e, 25: e},
   230  	},
   231  	"prepend": {
   232  		text:   map[string]struct{}{"prepend": e},
   233  		binary: map[byte]struct{}{15: e, 26: e},
   234  	},
   235  	"cas": {
   236  		text:   map[string]struct{}{"cas": e},
   237  		binary: map[byte]struct{}{},
   238  	},
   239  	"incr": {
   240  		text:   map[string]struct{}{"incr": e},
   241  		binary: map[byte]struct{}{5: e, 21: e},
   242  	},
   243  	"decr": {
   244  		text:   map[string]struct{}{"decr": e},
   245  		binary: map[byte]struct{}{6: e, 22: e},
   246  	},
   247  	"storage": {
   248  		text: map[string]struct{}{
   249  			"add":     e,
   250  			"set":     e,
   251  			"replace": e,
   252  			"append":  e,
   253  			"prepend": e,
   254  			"cas":     e,
   255  			"incr":    e,
   256  			"decr":    e,
   257  		},
   258  		binary: map[byte]struct{}{
   259  			1:  e,
   260  			2:  e,
   261  			3:  e,
   262  			5:  e,
   263  			6:  e,
   264  			17: e,
   265  			18: e,
   266  			19: e,
   267  			21: e,
   268  			22: e,
   269  			25: e,
   270  			26: e,
   271  		},
   272  	},
   273  
   274  	"get": {
   275  		text: map[string]struct{}{"get": e, "gets": e},
   276  		binary: map[byte]struct{}{
   277  			0:  e,
   278  			9:  e,
   279  			12: e,
   280  			13: e,
   281  		},
   282  	},
   283  
   284  	"delete": {
   285  		text: map[string]struct{}{"delete": e},
   286  		binary: map[byte]struct{}{
   287  			4:  e,
   288  			20: e,
   289  		},
   290  	},
   291  
   292  	"touch": {
   293  		text:   map[string]struct{}{"touch": e},
   294  		binary: map[byte]struct{}{28: e},
   295  	},
   296  
   297  	"gat": {
   298  		text:   map[string]struct{}{"gat": e, "gats": e},
   299  		binary: map[byte]struct{}{29: e, 30: e},
   300  	},
   301  
   302  	"writeGroup": {
   303  		text: map[string]struct{}{
   304  			"add":     e,
   305  			"set":     e,
   306  			"replace": e,
   307  			"append":  e,
   308  			"prepend": e,
   309  			"cas":     e,
   310  			"incr":    e,
   311  			"decr":    e,
   312  			"delete":  e,
   313  			"touch":   e,
   314  		},
   315  		binary: map[byte]struct{}{
   316  			1:  e,
   317  			2:  e,
   318  			3:  e,
   319  			4:  e,
   320  			5:  e,
   321  			6:  e,
   322  			17: e,
   323  			18: e,
   324  			19: e,
   325  			20: e,
   326  			21: e,
   327  			22: e,
   328  			25: e,
   329  			26: e,
   330  			28: e,
   331  		},
   332  	},
   333  
   334  	"slabs": {
   335  		text:   map[string]struct{}{"slabs": e},
   336  		binary: map[byte]struct{}{},
   337  	},
   338  
   339  	"lru": {
   340  		text:   map[string]struct{}{"lru": e},
   341  		binary: map[byte]struct{}{},
   342  	},
   343  
   344  	"lru_crawler": {
   345  		text:   map[string]struct{}{"lru_crawler": e},
   346  		binary: map[byte]struct{}{},
   347  	},
   348  
   349  	"watch": {
   350  		text:   map[string]struct{}{"watch": e},
   351  		binary: map[byte]struct{}{},
   352  	},
   353  
   354  	"stats": {
   355  		text:   map[string]struct{}{"stats": e},
   356  		binary: map[byte]struct{}{16: e},
   357  	},
   358  
   359  	"flush_all": {
   360  		text:   map[string]struct{}{"flush_all": e},
   361  		binary: map[byte]struct{}{8: e, 24: e},
   362  	},
   363  
   364  	"cache_memlimit": {
   365  		text:   map[string]struct{}{"cache_memlimit": e},
   366  		binary: map[byte]struct{}{},
   367  	},
   368  
   369  	"version": {
   370  		text:   map[string]struct{}{"version": e},
   371  		binary: map[byte]struct{}{11: e},
   372  	},
   373  
   374  	"misbehave": {
   375  		text:   map[string]struct{}{"misbehave": e},
   376  		binary: map[byte]struct{}{},
   377  	},
   378  
   379  	"quit": {
   380  		text:   map[string]struct{}{"quit": e},
   381  		binary: map[byte]struct{}{7: e, 23: e},
   382  	},
   383  
   384  	"noop": {
   385  		text:   map[string]struct{}{},
   386  		binary: map[byte]struct{}{10: e},
   387  	},
   388  	"verbosity": {
   389  		text:   map[string]struct{}{},
   390  		binary: map[byte]struct{}{27: e},
   391  	},
   392  	"sasl-list-mechs": {
   393  		text:   map[string]struct{}{},
   394  		binary: map[byte]struct{}{32: e},
   395  	},
   396  	"sasl-auth": {
   397  		text:   map[string]struct{}{},
   398  		binary: map[byte]struct{}{33: e}},
   399  	"sasl-step": {
   400  		text:   map[string]struct{}{},
   401  		binary: map[byte]struct{}{34: e}},
   402  	"rget": {
   403  		text:   map[string]struct{}{},
   404  		binary: map[byte]struct{}{48: e}},
   405  	"rset": {
   406  		text:   map[string]struct{}{},
   407  		binary: map[byte]struct{}{49: e}},
   408  	"rsetq": {
   409  		text:   map[string]struct{}{},
   410  		binary: map[byte]struct{}{50: e}},
   411  	"rappend": {
   412  		text:   map[string]struct{}{},
   413  		binary: map[byte]struct{}{51: e}},
   414  	"rappendq": {
   415  		text:   map[string]struct{}{},
   416  		binary: map[byte]struct{}{52: e}},
   417  	"rprepend": {
   418  		text:   map[string]struct{}{},
   419  		binary: map[byte]struct{}{53: e}},
   420  	"rprependq": {
   421  		text:   map[string]struct{}{},
   422  		binary: map[byte]struct{}{54: e}},
   423  	"rdelete": {
   424  		text:   map[string]struct{}{},
   425  		binary: map[byte]struct{}{55: e}},
   426  	"rdeleteq": {
   427  		text:   map[string]struct{}{},
   428  		binary: map[byte]struct{}{56: e}},
   429  	"rincr": {
   430  		text:   map[string]struct{}{},
   431  		binary: map[byte]struct{}{57: e}},
   432  	"rincrq": {
   433  		text:   map[string]struct{}{},
   434  		binary: map[byte]struct{}{58: e}},
   435  	"rdecr": {
   436  		text:   map[string]struct{}{},
   437  		binary: map[byte]struct{}{59: e}},
   438  	"rdecrq": {
   439  		text:   map[string]struct{}{},
   440  		binary: map[byte]struct{}{60: e}},
   441  	"set-vbucket": {
   442  		text:   map[string]struct{}{},
   443  		binary: map[byte]struct{}{61: e}},
   444  	"get-vbucket": {
   445  		text:   map[string]struct{}{},
   446  		binary: map[byte]struct{}{62: e}},
   447  	"del-vbucket": {
   448  		text:   map[string]struct{}{},
   449  		binary: map[byte]struct{}{63: e}},
   450  	"tap-connect": {
   451  		text:   map[string]struct{}{},
   452  		binary: map[byte]struct{}{64: e}},
   453  	"tap-mutation": {
   454  		text:   map[string]struct{}{},
   455  		binary: map[byte]struct{}{65: e}},
   456  	"tap-delete": {
   457  		text:   map[string]struct{}{},
   458  		binary: map[byte]struct{}{66: e}},
   459  	"tap-flush": {
   460  		text:   map[string]struct{}{},
   461  		binary: map[byte]struct{}{67: e}},
   462  	"tap-opaque": {
   463  		text:   map[string]struct{}{},
   464  		binary: map[byte]struct{}{68: e}},
   465  	"tap-vbucket-set": {
   466  		text:   map[string]struct{}{},
   467  		binary: map[byte]struct{}{69: e}},
   468  	"tap-checkpoint-start": {
   469  		text:   map[string]struct{}{},
   470  		binary: map[byte]struct{}{70: e}},
   471  	"tap-checkpoint-end": {
   472  		text:   map[string]struct{}{},
   473  		binary: map[byte]struct{}{71: e}},
   474  }