Openvswitch原理与代码分析: 添加一条流表flow

Posted 刘超的通俗云计算

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Openvswitch原理与代码分析: 添加一条流表flow相关的知识,希望对你有一定的参考价值。

添加一个flow,调用的命令为

ovs-ofctl add-flow hello "hard_timeout=0 idle_timeout=0 priority=1 table=21 pkt_mark=0x55 tun_id=0x55 actions=mod_nw_dst:192.168.56.101,output:2"

这里调用的是调用ovs/utilities/ovs-ofctl.c的命令行工具

这个命令行工具支持的所有的命令及处理函数定义如下:

  1. static const struct ovs_cmdl_command all_commands[] = {
  2. ????{ "show", "switch",
  3. ??????1, 1, ofctl_show },
  4. ????{ "monitor", "switch [misslen] [invalid_ttl] [watch:[...]]",
  5. ??????1, 3, ofctl_monitor },
  6. ????{ "snoop", "switch",
  7. ??????1, 1, ofctl_snoop },
  8. ????{ "dump-desc", "switch",
  9. ??????1, 1, ofctl_dump_desc },
  10. ????{ "dump-tables", "switch",
  11. ??????1, 1, ofctl_dump_tables },
  12. ????{ "dump-table-features", "switch",
  13. ??????1, 1, ofctl_dump_table_features },
  14. ????{ "dump-table-desc", "switch",
  15. ??????1, 1, ofctl_dump_table_desc },
  16. ????{ "dump-flows", "switch",
  17. ??????1, 2, ofctl_dump_flows },
  18. ????{ "dump-aggregate", "switch",
  19. ??????1, 2, ofctl_dump_aggregate },
  20. ????{ "queue-stats", "switch [port [queue]]",
  21. ??????1, 3, ofctl_queue_stats },
  22. ????{ "queue-get-config", "switch port",
  23. ??????2, 2, ofctl_queue_get_config },
  24. ????{ "add-flow", "switch flow",
  25. ??????2, 2, ofctl_add_flow },
  26. ????{ "add-flows", "switch file",
  27. ??????2, 2, ofctl_add_flows },
  28. ????{ "mod-flows", "switch flow",
  29. ??????2, 2, ofctl_mod_flows },
  30. ????{ "del-flows", "switch [flow]",
  31. ??????1, 2, ofctl_del_flows },
  32. ????{ "replace-flows", "switch file",
  33. ??????2, 2, ofctl_replace_flows },
  34. ????{ "diff-flows", "source1 source2",
  35. ??????2, 2, ofctl_diff_flows },
  36. ????{ "add-meter", "switch meter",
  37. ??????2, 2, ofctl_add_meter },
  38. ????{ "mod-meter", "switch meter",
  39. ??????2, 2, ofctl_mod_meter },
  40. ????{ "del-meter", "switch meter",
  41. ??????2, 2, ofctl_del_meters },
  42. ????{ "del-meters", "switch",
  43. ??????1, 1, ofctl_del_meters },
  44. ????{ "dump-meter", "switch meter",
  45. ??????2, 2, ofctl_dump_meters },
  46. ????{ "dump-meters", "switch",
  47. ??????1, 1, ofctl_dump_meters },
  48. ????{ "meter-stats", "switch [meter]",
  49. ??????1, 2, ofctl_meter_stats },
  50. ????{ "meter-features", "switch",
  51. ??????1, 1, ofctl_meter_features },
  52. ????{ "packet-out", "switch in_port actions packet...",
  53. ??????4, INT_MAX, ofctl_packet_out },
  54. ????{ "dump-ports", "switch [port]",
  55. ??????1, 2, ofctl_dump_ports },
  56. ????{ "dump-ports-desc", "switch [port]",
  57. ??????1, 2, ofctl_dump_ports_desc },
  58. ????{ "mod-port", "switch iface act",
  59. ??????3, 3, ofctl_mod_port },
  60. ????{ "mod-table", "switch mod",
  61. ??????3, 3, ofctl_mod_table },
  62. ????{ "get-frags", "switch",
  63. ??????1, 1, ofctl_get_frags },
  64. ????{ "set-frags", "switch frag_mode",
  65. ??????2, 2, ofctl_set_frags },
  66. ????{ "probe", "target",
  67. ??????1, 1, ofctl_probe },
  68. ????{ "ping", "target [n]",
  69. ??????1, 2, ofctl_ping },
  70. ????{ "benchmark", "target n count",
  71. ??????3, 3, ofctl_benchmark },
  72. ?
  73. ????{ "ofp-parse", "file",
  74. ??????1, 1, ofctl_ofp_parse },
  75. ????{ "ofp-parse-pcap", "pcap",
  76. ??????1, INT_MAX, ofctl_ofp_parse_pcap },
  77. ?
  78. ????{ "add-group", "switch group",
  79. ??????1, 2, ofctl_add_group },
  80. ????{ "add-groups", "switch file",
  81. ??????1, 2, ofctl_add_groups },
  82. ????{ "mod-group", "switch group",
  83. ??????1, 2, ofctl_mod_group },
  84. ????{ "del-groups", "switch [group]",
  85. ??????1, 2, ofctl_del_groups },
  86. ????{ "insert-buckets", "switch [group]",
  87. ??????1, 2, ofctl_insert_bucket },
  88. ????{ "remove-buckets", "switch [group]",
  89. ??????1, 2, ofctl_remove_bucket },
  90. ????{ "dump-groups", "switch [group]",
  91. ??????1, 2, ofctl_dump_group_desc },
  92. ????{ "dump-group-stats", "switch [group]",
  93. ??????1, 2, ofctl_dump_group_stats },
  94. ????{ "dump-group-features", "switch",
  95. ??????1, 1, ofctl_dump_group_features },
  96. ????{ "add-tlv-map", "switch map",
  97. ??????2, 2, ofctl_add_tlv_map },
  98. ????{ "del-tlv-map", "switch [map]",
  99. ??????1, 2, ofctl_del_tlv_map },
  100. ????{ "dump-tlv-map", "switch",
  101. ??????1, 1, ofctl_dump_tlv_map },
  102. ????{ "help", NULL, 0, INT_MAX, ofctl_help },
  103. ????{ "list-commands", NULL, 0, INT_MAX, ofctl_list_commands },
  104. ?
  105. ????/* Undocumented commands for testing. */
  106. ????{ "parse-flow", NULL, 1, 1, ofctl_parse_flow },
  107. ????{ "parse-flows", NULL, 1, 1, ofctl_parse_flows },
  108. ????{ "parse-nx-match", NULL, 0, 0, ofctl_parse_nxm },
  109. ????{ "parse-nxm", NULL, 0, 0, ofctl_parse_nxm },
  110. ????{ "parse-oxm", NULL, 1, 1, ofctl_parse_oxm },
  111. ????{ "parse-actions", NULL, 1, 1, ofctl_parse_actions },
  112. ????{ "parse-instructions", NULL, 1, 1, ofctl_parse_instructions },
  113. ????{ "parse-ofp10-match", NULL, 0, 0, ofctl_parse_ofp10_match },
  114. ????{ "parse-ofp11-match", NULL, 0, 0, ofctl_parse_ofp11_match },
  115. ????{ "parse-pcap", NULL, 1, 1, ofctl_parse_pcap },
  116. ????{ "check-vlan", NULL, 2, 2, ofctl_check_vlan },
  117. ????{ "print-error", NULL, 1, 1, ofctl_print_error },
  118. ????{ "encode-error-reply", NULL, 2, 2, ofctl_encode_error_reply },
  119. ????{ "ofp-print", NULL, 1, 2, ofctl_ofp_print },
  120. ????{ "encode-hello", NULL, 1, 1, ofctl_encode_hello },
  121. ?
  122. ????{ NULL, NULL, 0, 0, NULL },
  123. };

?

根据这个数据结构的定义,"add-flow"调用的函数为

  1. static void
  2. ofctl_add_flow(struct ovs_cmdl_context *ctx)
  3. {
  4. ????ofctl_flow_mod(ctx->argc, ctx->argv, OFPFC_ADD);
  5. }

?

调用ofctl_flow_mod,parse_ofp_flow_mod_str将字符串解析为ofputil_flow_mod fm

ofputil_flow_mod包含两个最重要的成员变量:

struct match match,所谓match就是一个key。

struct ofpact *ofpacts; /* Series of "struct ofpact"s. */

?

  1. static void
  2. ofctl_flow_mod(int argc, char *argv[], uint16_t command)
  3. {
  4. ????if (argc > 2 && !strcmp(argv[2], "-")) {
  5. ????????ofctl_flow_mod_file(argc, argv, command);
  6. ????} else {
  7. ????????struct ofputil_flow_mod fm;
  8. ????????char *error;
  9. ????????enum ofputil_protocol usable_protocols;
  10. ?
  11. ????????error = parse_ofp_flow_mod_str(&fm, argc > 2 ? argv[2] : "", command,
  12. ???????????????????????????????????????&usable_protocols);
  13. ????????if (error) {
  14. ????????????ovs_fatal(0, "%s", error);
  15. ????????}
  16. ????????ofctl_flow_mod__(argv[1], &fm, 1, usable_protocols);
  17. ????}
  18. }

?

ofctl_flow_mod__会打开一个指向ovs-vswitchd的socket,将flow match变成openflow的协议,发出去transact_noreply

  1. static void
  2. ofctl_flow_mod__(const char *remote, struct ofputil_flow_mod *fms,
  3. ?????????????????size_t n_fms, enum ofputil_protocol usable_protocols)
  4. {
  5. ????enum ofputil_protocol protocol;
  6. ????struct vconn *vconn;
  7. ????size_t i;
  8. ?
  9. ????if (bundle) {
  10. ????????bundle_flow_mod__(remote, fms, n_fms, usable_protocols);
  11. ????????return;
  12. ????}
  13. ?
  14. ????protocol = open_vconn_for_flow_mod(remote, &vconn, usable_protocols);
  15. ?
  16. ????for (i = 0; i < n_fms; i++) {
  17. ????????struct ofputil_flow_mod *fm = &fms[i];
  18. ?
  19. ????????transact_noreply(vconn, ofputil_encode_flow_mod(fm, protocol));
  20. ????????free(CONST_CAST(struct ofpact *, fm->ofpacts));
  21. ????}
  22. ????vconn_close(vconn);
  23. }

?

Ovs-vswitchd会监听socket,在ovs-vswitchd.c中bridge_run每个bridge会监听消息,ofproto_run监听openflow的调用,connmgr_run网络连接管理,ofconn_run管理socket连接。

connmgr_run(p->connmgr, handle_openflow);会设置当有openflow调用的时候,handle_openflow会被调用。

  1. static void
  2. handle_openflow(struct ofconn *ofconn, const struct ofpbuf *ofp_msg)
  3. ????OVS_EXCLUDED(ofproto_mutex)
  4. {
  5. ????enum ofperr error = handle_openflow__(ofconn, ofp_msg);
  6. ?
  7. ????if (error) {
  8. ????????ofconn_send_error(ofconn, ofp_msg->data, error);
  9. ????}
  10. ????COVERAGE_INC(ofproto_recv_openflow);
  11. }

?

handle_openflow__会做如下的调用:

  1. case OFPTYPE_FLOW_MOD:
  2. ????return handle_flow_mod(ofconn, oh);

?

handle_flow_mod首先将openflow协议解析为fm和ofpacts

  1. error = ofputil_decode_flow_mod(&ofm.fm, oh, ofconn_get_protocol(ofconn),
  2. ????????????????????????????????&ofpacts,
  3. ????????????????????????????????u16_to_ofp(ofproto->max_ports),
  4. ????????????????????????????????ofproto->n_tables);

?

然后调用static enum ofperr handle_flow_mod__(struct ofproto *ofproto, struct ofproto_flow_mod *ofm, const struct flow_mod_requester *req)

会调用static enum ofperr ofproto_flow_mod_start(struct ofproto *ofproto, struct ofproto_flow_mod *ofm) OVS_REQUIRES(ofproto_mutex)

  1. static enum ofperr
  2. ofproto_flow_mod_start(struct ofproto *ofproto, struct ofproto_flow_mod *ofm)
  3. ????OVS_REQUIRES(ofproto_mutex)
  4. {
  5. ????switch (ofm->fm.command) {
  6. ????case OFPFC_ADD:
  7. ????????return add_flow_start(ofproto, ofm);
  8. ????????/* , &be->old_rules.stub[0],
  9. ???????????&be->new_rules.stub[0]); */
  10. ????case OFPFC_MODIFY:
  11. ????????return modify_flows_start_loose(ofproto, ofm);
  12. ????case OFPFC_MODIFY_STRICT:
  13. ????????return modify_flow_start_strict(ofproto, ofm);
  14. ????case OFPFC_DELETE:
  15. ????????return delete_flows_start_loose(ofproto, ofm);
  16. ?
  17. ????case OFPFC_DELETE_STRICT:
  18. ????????return delete_flow_start_strict(ofproto, ofm);
  19. ????}
  20. ?
  21. ????return OFPERR_OFPFMFC_BAD_COMMAND;
  22. }

?

在函数add_flow_start中,首先cls_rule_init(&cr, &fm->match, fm->priority); 将match也即key变成一个cls_rule,cls_rule是一个压缩版本的match,match是一个整个数据结构保存整个package,从L1一直到L4全都有,比较大,如果保存在内存太浪费,cls_rule中有一个minimatch,是用压缩的方式保存match,也即如果match中为0的部分不保存,采取稀疏矩阵的方式。

接下来创建一个新的rule,error = replace_rule_create(ofproto, fm, &cr, table - ofproto->tables, rule, new_rule);

最后replace_rule_start(ofproto, ofm->version, rule, *new_rule, conjs, n_conjs); 将rule替换现在的rule,有则替换,没有则插入。

  1. static void
  2. replace_rule_start(struct ofproto *ofproto, cls_version_t version,
  3. ???????????????????struct rule *old_rule, struct rule *new_rule,
  4. ???????????????????struct cls_conjunction *conjs, size_t n_conjs)
  5. {
  6. ????struct oftable *table = &ofproto->tables[new_rule->table_id];
  7. ?
  8. ????/* ‘old_rule‘ may be either an evicted rule or replaced rule. */
  9. ????if (old_rule) {
  10. ????????/* Mark the old rule for removal in the next version. */
  11. ????????cls_rule_make_invisible_in_version(&old_rule->cr, version);
  12. ????} else {
  13. ????????table->n_flows++;
  14. ????}
  15. ????/* Insert flow to the classifier, so that later flow_mods may relate
  16. ?????* to it. This is reversible, in case later errors require this to
  17. ?????* be reverted. */
  18. ????ofproto_rule_insert__(ofproto, new_rule);
  19. ????/* Make the new rule visible for classifier lookups only from the next
  20. ?????* version. */
  21. ????classifier_insert(&table->cls, &new_rule->cr, version, conjs, n_conjs);
  22. }

以上是关于Openvswitch原理与代码分析: 添加一条流表flow的主要内容,如果未能解决你的问题,请参考以下文章

Openvswitch原理与代码分析: 修改Openvswitch代码添加自定义action

Openvswitch原理与代码分析: openvswitch内核模块的加载

Openvswitch原理与代码分析:总体架构

Openvswitch原理与代码分析:总体架构

Openvswitch原理与代码分析:网络包的处理过程

Openvswitch原理与代码分析: ovs-vswitchd的启动