Beispiel #1
0
        static void Main(string[] args)
        {
            //Future idea: create tool like iftop via API
            //Posible features:
            // DNS resolve
            // Geolocation - https://db-ip.com/db/
            // Visualise if address is from any AddressList
            // ??
            // looking for volunteers :-)

            using (ITikConnection connection = ConnectionFactory.CreateConnection(TikConnectionType.Api))
            {
                connection.Open(ConfigurationManager.AppSettings["host"], ConfigurationManager.AppSettings["user"], ConfigurationManager.AppSettings["pass"]);
                string interfaceName = ConfigurationManager.AppSettings["interface"];

                Console.Clear();

                var loadingContext = connection.LoadAsync <ToolTorch>(
                    TorchItemRead, error => Console.WriteLine(error.ToString()),
                    connection.CreateParameter("interface", interfaceName),
                    //connection.CreateParameter("ip-protocol", "any"),
                    connection.CreateParameter("port", "any"),
                    connection.CreateParameter("src-address", "0.0.0.0/0"),
                    connection.CreateParameter("dst-address", "0.0.0.0/0"));

                Console.ReadLine();

                loadingContext.Cancel();
            }
        }
Beispiel #2
0
        public static bool suspendpppoeuser(string pppoeuser)
        {
            using (ITikConnection connection = ConnectionFactory.CreateConnection(TikConnectionType.Api)) // Use TikConnectionType.Api for mikrotikversion prior v6.45
            {
                connection.Open("10.19.10.1", "OrionAdmin", "Frank1e2015");


                var pppoeactive = connection.LoadList <PppActive>();
                foreach (PppActive pppa in pppoeactive)
                {
                    string ipaddress = "";
                    if (pppa.Name == pppoeuser)
                    {
                        ipaddress = pppa.Address;
                    }
                    if (ipaddress != "")
                    {
                        ITikCommand cmd = connection.CreateCommand("/ip/firewall/address-list/add",
                                                                   connection.CreateParameter("list", "unmssuspend"),
                                                                   connection.CreateParameter("address", ipaddress),
                                                                   connection.CreateParameter("comment", "suspend"));
                        cmd.ExecuteAsync(response =>
                        {
                            // Console.WriteLine("Row: " + response.GetResponseField("tx"));
                        });

                        cmd.Cancel();
                    }
                }

                connection.Close();
            }
            return(true);
        }
        public List <ToolPing> Ping(string address, short count = 1, short size = 64)
        {
            List <ToolPing> responseList      = new List <ToolPing>();
            Exception       responseException = null;

            try
            {
                ITikCommand pingCommand = connection.LoadAsync <ToolPing>(ping =>
                                                                          responseList.Add(ping),
                                                                          exception => responseException = exception,
                                                                          connection.CreateParameter("address", address),
                                                                          connection.CreateParameter("count", count.ToString()),
                                                                          connection.CreateParameter("size", "64")
                                                                          );

                Thread.Sleep(2 * 1000);
                if (responseException != null)
                {
                    throw responseException;
                }
                return(responseList);
            }
            catch (Exception ex)
            {
                throw;
            }
        }
Beispiel #4
0
        public static InterfacePppoeClientMonitor GetInterfacePppoeClientMonitorSnapshot(this ITikConnection connection, string numbers)
        {
            var result = connection.LoadSingle <InterfacePppoeClientMonitor>(
                connection.CreateParameter("numbers", numbers, TikCommandParameterFormat.NameValue),
                connection.CreateParameter("once", "", TikCommandParameterFormat.NameValue));

            return(result);
        }
Beispiel #5
0
        /// <summary>
        /// Gets snapshot of actual traffic RX/TX values for given <paramref name="interfaceName"/>.
        /// </summary>
        public static InterfaceMonitorTraffic GetInterfaceMonitorTrafficSnapshot(this ITikConnection connection, string interfaceName)
        {
            var result = connection.LoadSingle <InterfaceMonitorTraffic>(
                connection.CreateParameter("interface", interfaceName, TikCommandParameterFormat.NameValue),
                connection.CreateParameter("once", "", TikCommandParameterFormat.NameValue));

            return(result);
        }
Beispiel #6
0
        /// <summary>
        /// Gets snapshot of actual values for given <paramref name="interfaceName"/>.
        /// </summary>
        public static EthernetMonitor GetEthernetMonitorSnapshot(this ITikConnection connection, string interfaceName)
        {
            var result = connection.LoadSingle <EthernetMonitor>(
                connection.CreateParameter("numbers", interfaceName, TikCommandParameterFormat.NameValue),
                connection.CreateParameter("once", "", TikCommandParameterFormat.NameValue));

            return(result);
        }
        /// <summary>
        /// Traceroutes given <see paramref="address"/>.
        /// </summary>
        public static IEnumerable <ToolTraceroute> HamnetTraceroute(this ITikConnection connection, string address)
        {
            var result = connection.LoadList <ToolTraceroute>(
                connection.CreateParameter("address", address, TikCommandParameterFormat.NameValue),
                connection.CreateParameter("count", "1", TikCommandParameterFormat.NameValue));

            return(result);
        }
Beispiel #8
0
        /// <summary>
        /// Pings given <see paramref="address"/>. Returns <paramref name="cnt"/> of ping results to the <paramref name="address"/>.
        /// </summary>
        public static IEnumerable <ToolPing> Ping(this ITikConnection connection, string address, int cnt)
        {
            var result = connection.LoadList <ToolPing>(
                connection.CreateParameter("address", address, TikCommandParameterFormat.NameValue),
                connection.CreateParameter("count", cnt.ToString(), TikCommandParameterFormat.NameValue));

            return(result);
        }
Beispiel #9
0
        private static void DeleteAddressList(ITikConnection connection)
        {
            var existingAddressList = connection.LoadList <FirewallAddressList>(
                connection.CreateParameter("list", listName),
                connection.CreateParameter("address", ipAddress)).SingleOrDefault();

            if (existingAddressList != null)
            {
                connection.Delete(existingAddressList);
            }
        }
Beispiel #10
0
        /// <summary>
        /// Adds a certain profile
        /// </summary>
        /// <param name="profile">The profile to add</param>
        /// <returns>The status of the adding process</returns>
        public Task <bool> AddProfileAsync(HotspotUserProfile profile) => Task.Run(() =>
        {
            try
            {
                //_connection.Save(profile);
                _connection.CreateCommand("/ip/hotspot/user/profile/add",
                                          _connection.CreateParameter("name", profile.Name),
                                          _connection.CreateParameter("shared-users", profile.SharedUsers),
                                          _connection.CreateParameter("rate-limit", profile.RateLimit)
                                          ).ExecuteNonQuery();

                return(true);
            }
            catch { return(false); }
        });
Beispiel #11
0
        private static void CreateOrUpdateAddressListMulti(ITikConnection connection)
        {
            var existingAddressList = connection.LoadList<FirewallAddressList>(
                connection.CreateParameter("list", listName)).ToList();
            var listClonedBackup = existingAddressList.CloneEntityList(); //creates clone of all entities in list

            if (existingAddressList.Count() <= 0)
            {
                //Create (just in memory)
                existingAddressList.Add(
                    new FirewallAddressList()
                    {
                        Address = ipAddress,
                        List = listName,
                    });
                existingAddressList.Add(
                    new FirewallAddressList()
                    {
                        Address = ipAddress2,
                        List = listName,
                    });
            }
            else
            {
                //Update (just in memory)
                foreach (var addressList in existingAddressList)
                {
                    addressList.Comment = "Comment update: " + DateTime.Now.ToShortTimeString();
                }
            }

            //save differences into mikrotik  (existingAddressList=modified, listClonedBackup=unmodified)
            connection.SaveListDifferences(existingAddressList, listClonedBackup);
        }
Beispiel #12
0
        private static void CreateOrUpdateAddressListMulti(ITikConnection connection)
        {
            var existingAddressList = connection.LoadList <FirewallAddressList>(
                connection.CreateParameter("list", listName)).ToList();
            var listClonedBackup = existingAddressList.CloneEntityList(); //creates clone of all entities in list

            if (existingAddressList.Count() <= 0)
            {
                //Create (just in memory)
                existingAddressList.Add(
                    new FirewallAddressList()
                {
                    Address = ipAddress,
                    List    = listName,
                });
                existingAddressList.Add(
                    new FirewallAddressList()
                {
                    Address = ipAddress2,
                    List    = listName,
                });
            }
            else
            {
                //Update (just in memory)
                foreach (var addressList in existingAddressList)
                {
                    addressList.Comment = "Comment update: " + DateTime.Now.ToShortTimeString();
                }
            }

            //save differences into mikrotik  (existingAddressList=modified, listClonedBackup=unmodified)
            connection.SaveListDifferences(existingAddressList, listClonedBackup);
        }
Beispiel #13
0
        private static void WriteToLog(ITikConnection connection, string message, string logLevelCommandSufix)
        {
            var cmd = connection.CreateCommand("/log/" + logLevelCommandSufix,
                                               connection.CreateParameter("message", message));

            cmd.ExecuteNonQuery();
        }
Beispiel #14
0
        private static void Torch(ITikConnection connection)
        {
            ITikCommand torchCmd = connection.CreateCommand("/tool/torch",
                                                            connection.CreateParameter("interface", "ether1"),
                                                            connection.CreateParameter("port", "any"),
                                                            connection.CreateParameter("src-address", "0.0.0.0/0"),
                                                            connection.CreateParameter("dst-address", "0.0.0.0/0")
                                                            );

            torchCmd.ExecuteAsync(response =>
            {
                Console.WriteLine("Row: " + response.GetResponseField("tx"));
            });
            Console.WriteLine("Press ENTER");
            Console.ReadLine();
            torchCmd.CancelAndJoin();
        }
Beispiel #15
0
        private static void PrintAddressList(ITikConnection connection)
        {
            var addressLists = connection.LoadList <FirewallAddressList>(
                connection.CreateParameter("list", listName));

            foreach (FirewallAddressList addressList in addressLists)
            {
                Console.WriteLine("{0}{1}: {2} {3} ({4})", addressList.Disabled ? "X" : " ", addressList.Dynamic ? "D" : " ", addressList.Address, addressList.List, addressList.Comment);
            }
        }
Beispiel #16
0
        private static void DeleteAddressListMulti(ITikConnection connection)
        {
            var existingAddressList = connection.LoadList <FirewallAddressList>(
                connection.CreateParameter("list", listName)).ToList();
            var listClonedBackup = existingAddressList.CloneEntityList(); //creates clone of all entities in list

            existingAddressList.Clear();

            //save differences into mikrotik  (existingAddressList=modified, listClonedBackup=unmodified)
            connection.SaveListDifferences(existingAddressList, listClonedBackup);
        }
Beispiel #17
0
        private static void CreateOrUpdateAddressList(ITikConnection connection)
        {
            var existingAddressList = connection.LoadList<FirewallAddressList>(
                connection.CreateParameter("list", listName),
                connection.CreateParameter("address", ipAddress)).SingleOrDefault();
            if (existingAddressList == null)
            {
                //Create
                var newAddressList = new FirewallAddressList()
                {
                    Address = ipAddress,
                    List = listName,
                };
                connection.Save(newAddressList);
            }
            else
            {
                //Update
                existingAddressList.Comment = "Comment update: " + DateTime.Now.ToShortTimeString();

                connection.Save(existingAddressList);
            }
        }
Beispiel #18
0
        public static bool UpdateSecret()
        {
            using (ITikConnection connection = ConnectionFactory.CreateConnection(TikConnectionType.Api)) // Use TikConnectionType.Api for mikrotikversion prior v6.45
            {
                connection.Open("10.19.60.1", "OrionAdmin", "Frank1e2015");

                radiusEntities     db            = new radiusEntities();
                List <RadiusEntry> radiusentries = db.RadiusEntries.ToList();
                foreach (RadiusEntry re in radiusentries)
                {
                    ITikCommand cmd = connection.CreateCommand("/ppp/secret/add",
                                                               connection.CreateParameter("name", re.username),
                                                               connection.CreateParameter("password", re.value),
                                                               connection.CreateParameter("profile", re.groupname));
                    cmd.ExecuteAsync(response =>
                    {
                        // Console.WriteLine("Row: " + response.GetResponseField("tx"));
                    });

                    cmd.Cancel();
                }
                return(true);
            }
        }
Beispiel #19
0
        private static void CreateOrUpdateAddressList(ITikConnection connection)
        {
            var existingAddressList = connection.LoadList <FirewallAddressList>(
                connection.CreateParameter("list", listName),
                connection.CreateParameter("address", ipAddress)).SingleOrDefault();

            if (existingAddressList == null)
            {
                //Create
                var newAddressList = new FirewallAddressList()
                {
                    Address = ipAddress,
                    List    = listName,
                };
                connection.Save(newAddressList);
            }
            else
            {
                //Update
                existingAddressList.Comment = "Comment update: " + DateTime.Now.ToShortTimeString();

                connection.Save(existingAddressList);
            }
        }
Beispiel #20
0
        public static bool Mikrotik()

        {
            using (ITikConnection connection = ConnectionFactory.CreateConnection(TikConnectionType.Api)) // Use TikConnectionType.Api for mikrotikversion prior v6.45
            {
                connection.Open("10.19.20.1", "OrionAdmin", "Frank1e2015");
                ITikCommand cmd      = connection.CreateCommand("/system/identity/print");
                var         identity = cmd.ExecuteScalar();
                Console.WriteLine("Identity: {0}", identity);
                var logs = connection.LoadList <Log>();
                foreach (Log log in logs)
                {
                    Console.WriteLine("{0}[{1}]: {2}", log.Time, log.Topics, log.Message);
                }
                var firewallFilter = new FirewallFilter()
                {
                    Chain  = FirewallFilter.ChainType.Forward,
                    Action = FirewallFilter.ActionType.Accept,
                };
                connection.Save(firewallFilter);
                ITikCommand torchCmd = connection.CreateCommand("/tool/torch",
                                                                connection.CreateParameter("interface", "ether1"),
                                                                connection.CreateParameter("port", "any"),
                                                                connection.CreateParameter("src-address", "0.0.0.0/0"),
                                                                connection.CreateParameter("dst-address", "0.0.0.0/0"));

                torchCmd.ExecuteAsync(response =>
                {
                    Console.WriteLine("Row: " + response.GetResponseField("tx"));
                });
                Console.WriteLine("Press ENTER");
                Console.ReadLine();
                torchCmd.Cancel();
                return(true);
            }
        }
Beispiel #21
0
        /// <summary>
        /// Loads entity with specified name. Returns null if not found.
        /// </summary>
        /// <typeparam name="TEntity">Loaded entities type.</typeparam>
        /// <param name="connection">Tik connection used to load.</param>
        /// <param name="name">Entity name.</param>
        /// <returns>Loaded entity or null.</returns>
        /// <exception cref="InvalidOperationException">Connection or command text not set. Comand is already running. Connection is not opened. Invalid response from API.</exception>
        /// <exception cref="TikCommandTrapException">!trap returned from API call.</exception>
        /// <exception cref="TikCommandFatalException">!fatal returned from API call.</exception>
        /// <exception cref="TikCommandUnexpectedResponseException">Unexpected response from mikrotik (multiple returned rows, missing !done row etc.)</exception>
        /// <exception cref="TikNoSuchCommandException">Invalid mikrotik command (syntax error). Mikrotik API message: 'no such command'</exception>
        /// <exception cref="TikNoSuchItemException">Invalid item (bad id/name etc.). Mikrotik API message: 'no such item'.</exception>
        /// <exception cref="TikCommandAmbiguousResultException">More than one row returned.</exception>
        public static TEntity LoadByName <TEntity>(this ITikConnection connection, string name)
            where TEntity : new()
        {
            var command    = CreateLoadCommandWithFilter <TEntity>(connection, connection.CreateParameter("name", name));
            var candidates = command.LoadList <TEntity>();

            var cnt = candidates.Count();

            if (cnt == 0)
            {
                throw new TikNoSuchItemException(command);
            }
            else if (cnt > 1)
            {
                throw new TikCommandAmbiguousResultException(command, cnt);
            }
            else
            {
                return(candidates.Single());
            }
        }
Beispiel #22
0
 private static void Torch(ITikConnection connection)
 {
     ITikCommand torchCmd = connection.CreateCommand("/tool/torch",
         connection.CreateParameter("interface", "ether1"),
         connection.CreateParameter("port", "any"),
         connection.CreateParameter("src-address", "0.0.0.0/0"),
         connection.CreateParameter("dst-address", "0.0.0.0/0")
         );
     torchCmd.ExecuteAsync(response =>
     {
         Console.WriteLine("Row: " + response.GetResponseField("tx"));
     });
     Console.WriteLine("Press ENTER");
     Console.ReadLine();
     torchCmd.CancelAndJoin();
 }
Beispiel #23
0
 private static void PrintAddressList(ITikConnection connection)
 {
     var addressLists = connection.LoadList<FirewallAddressList>(
         connection.CreateParameter("list", listName));
     foreach (FirewallAddressList addressList in addressLists)
     {
         Console.WriteLine("{0}{1}: {2} {3} ({4})", addressList.Disabled ? "X" : " ", addressList.Dynamic ? "D" : " ", addressList.Address, addressList.List, addressList.Comment);
     }
 }
Beispiel #24
0
        private static void FirewallMangleMerge(ITikConnection connection)
        {
            //manage just subset before rules marked with comment =START= and =END=

            //Create subset boundaries if not present
            const string startComment = "=START=";
            const string endComment = "=END=";
            var startMangle = connection.LoadSingleOrDefault<FirewallMangle>(connection.CreateParameter("comment", startComment));
            if (startMangle == null)
            {
                startMangle = new FirewallMangle()
                {
                    Chain = "forward",
                    Action = FirewallMangle.ActionType.Passthrough,
                    Comment = startComment,
                    Disabled = true,
                };
                connection.Save(startMangle);
            };
            var endMangle = connection.LoadSingleOrDefault<FirewallMangle>(connection.CreateParameter("comment", endComment));
            if (endMangle == null)
            {
                endMangle = new FirewallMangle()
                {
                    Chain = "forward",
                    Action = FirewallMangle.ActionType.Passthrough,
                    Comment = endComment,
                    Disabled = true,
                };
                connection.Save(endMangle);
            };

            //Merge subset between boundaries
            string unique = Guid.NewGuid().ToString();
            List<FirewallMangle> original = connection.LoadAll<FirewallMangle>().SkipWhile(m=>m.Comment != startComment).TakeWhile(m=>m.Comment != endComment)
                .Concat(new List<FirewallMangle> { endMangle})
                .ToList(); //just subset between =START= and =END= (not very elegant but functional and short ;-) )
            List<FirewallMangle> expected = new List<FirewallMangle>();
            expected.Add(startMangle);
            expected.Add(new FirewallMangle()
            {
                Chain = "forward",
                SrcAddress = "192.168.1.1",
                Action = FirewallMangle.ActionType.MarkPacket,
                NewPacketMark = "mark-001",
                Passthrough = false,
            });
            expected.Add(new FirewallMangle()
            {
                Chain = "forward",
                SrcAddress = "192.168.1.2",
                Action = FirewallMangle.ActionType.MarkPacket,
                NewPacketMark = "mark-002" + "-" +  unique,
                Passthrough = false,
            });
            expected.Add(new FirewallMangle()
            {
                Chain = "forward",
                SrcAddress = "192.168.1.3",
                Action = FirewallMangle.ActionType.MarkPacket,
                NewPacketMark = "mark-003",
                Passthrough = false,
                Comment = unique,
            });
            expected.Add(endMangle);

            connection.CreateMerge(expected, original)
                .WithKey(mangle => mangle.SrcAddress + ":" + mangle.Comment) //Use src-address as key
                .Field(q => q.Chain)
                .Field(q => q.SrcAddress) //Do not forget include also key fields !!!
                .Field(q => q.Action)
                .Field(q => q.NewPacketMark)
                .Field(q => q.Passthrough)
                .Field(q => q.Comment)
                .Save();
        }
Beispiel #25
0
        private static void DeleteAddressListMulti(ITikConnection connection)
        {
            var existingAddressList = connection.LoadList<FirewallAddressList>(
                connection.CreateParameter("list", listName)).ToList();
            var listClonedBackup = existingAddressList.CloneEntityList(); //creates clone of all entities in list

            existingAddressList.Clear();

            //save differences into mikrotik  (existingAddressList=modified, listClonedBackup=unmodified)
            connection.SaveListDifferences(existingAddressList, listClonedBackup);
        }
Beispiel #26
0
        private static void DeleteAddressList(ITikConnection connection)
        {
            var existingAddressList = connection.LoadList<FirewallAddressList>(
                connection.CreateParameter("list", listName),
                connection.CreateParameter("address", ipAddress)).SingleOrDefault();

            if (existingAddressList != null)
                connection.Delete(existingAddressList);
        }
Beispiel #27
0
 private static void WriteToLog(ITikConnection connection, string message, string logLevelCommandSufix)
 {
     var cmd =connection.CreateCommand("/log/" + logLevelCommandSufix,
         connection.CreateParameter("message", message));
     cmd.ExecuteNonQuery();
 }
 public Task <bool> ActivateUser(UserManagerUser user, string profileName) => Task.Run(() =>
 {
     try
     {
         _connection.CreateCommand("/tool/user-manager/user/create-and-activate-profile", _connection.CreateParameter("user", user.Name), _connection.CreateParameter("customer", user.Customer), _connection.CreateParameter("profile", profileName)).ExecuteAsync(null);
         return(true);
     }
     catch
     {
         return(false);
     }
 });
 /// <summary>
 /// Loads entity with specified id. Returns null if not found.
 /// </summary>
 /// <typeparam name="TEntity">Loaded entities type.</typeparam>
 /// <param name="connection">Tik connection used to load.</param>
 /// <param name="id">Entity id.</param>
 /// <returns>Loaded entity or null.</returns>
 public static TEntity LoadById <TEntity>(this ITikConnection connection, string id)
     where TEntity : new()
 {
     return(LoadList <TEntity>(connection, connection.CreateParameter(TikSpecialProperties.Id, id)).SingleOrDefault());
 }
Beispiel #30
0
        /// <summary>
        /// Factory method - creates parameters instance specific for connection and command type. Shortcut for a .proplist parameter.
        /// </summary>
        /// <param name="proplist">Names of the wanted properties</param>
        /// <returns>Created parameter with name .proplist and a comma separated property list as value.</returns>
        /// <seealso cref="ITikCommand.Parameters"/>
        public static ITikCommandParameter CreateProplistParameter(this ITikConnection connection, params string[] proplist)
        {
            var result = connection.CreateParameter(TikSpecialProperties.Proplist, string.Join(",", proplist), TikCommandParameterFormat.NameValue);

            return(result);
        }
Beispiel #31
0
        private static void FirewallMangleMerge(ITikConnection connection)
        {
            //manage just subset before rules marked with comment =START= and =END=

            //Create subset boundaries if not present
            const string startComment = "=START=";
            const string endComment   = "=END=";
            var          startMangle  = connection.LoadSingleOrDefault <FirewallMangle>(connection.CreateParameter("comment", startComment));

            if (startMangle == null)
            {
                startMangle = new FirewallMangle()
                {
                    Chain    = "forward",
                    Action   = FirewallMangle.ActionType.Passthrough,
                    Comment  = startComment,
                    Disabled = true,
                };
                connection.Save(startMangle);
            }
            ;
            var endMangle = connection.LoadSingleOrDefault <FirewallMangle>(connection.CreateParameter("comment", endComment));

            if (endMangle == null)
            {
                endMangle = new FirewallMangle()
                {
                    Chain    = "forward",
                    Action   = FirewallMangle.ActionType.Passthrough,
                    Comment  = endComment,
                    Disabled = true,
                };
                connection.Save(endMangle);
            }
            ;

            //Merge subset between boundaries
            string unique = Guid.NewGuid().ToString();
            List <FirewallMangle> original = connection.LoadAll <FirewallMangle>().SkipWhile(m => m.Comment != startComment).TakeWhile(m => m.Comment != endComment)
                                             .Concat(new List <FirewallMangle> {
                endMangle
            })
                                             .ToList(); //just subset between =START= and =END= (not very elegant but functional and short ;-) )
            List <FirewallMangle> expected = new List <FirewallMangle>();

            expected.Add(startMangle);
            expected.Add(new FirewallMangle()
            {
                Chain         = "forward",
                SrcAddress    = "192.168.1.1",
                Action        = FirewallMangle.ActionType.MarkPacket,
                NewPacketMark = "mark-001",
                Passthrough   = false,
            });
            expected.Add(new FirewallMangle()
            {
                Chain         = "forward",
                SrcAddress    = "192.168.1.2",
                Action        = FirewallMangle.ActionType.MarkPacket,
                NewPacketMark = "mark-002" + "-" + unique,
                Passthrough   = false,
            });
            expected.Add(new FirewallMangle()
            {
                Chain         = "forward",
                SrcAddress    = "192.168.1.3",
                Action        = FirewallMangle.ActionType.MarkPacket,
                NewPacketMark = "mark-003",
                Passthrough   = false,
                Comment       = unique,
            });
            expected.Add(endMangle);

            connection.CreateMerge(expected, original)
            .WithKey(mangle => mangle.SrcAddress + ":" + mangle.Comment) //Use src-address as key
            .Field(q => q.Chain)
            .Field(q => q.SrcAddress)                                    //Do not forget include also key fields !!!
            .Field(q => q.Action)
            .Field(q => q.NewPacketMark)
            .Field(q => q.Passthrough)
            .Field(q => q.Comment)
            .Save();
        }
 /// <summary>
 /// Loads entity with specified name. Returns null if not found.
 /// </summary>
 /// <typeparam name="TEntity">Loaded entities type.</typeparam>
 /// <param name="connection">Tik connection used to load.</param>
 /// <param name="name">Entity name.</param>
 /// <returns>Loaded entity or null.</returns>
 public static TEntity LoadByName <TEntity>(this ITikConnection connection, string name)
     where TEntity : new()
 {
     return(LoadList <TEntity>(connection, connection.CreateParameter("name", name)).SingleOrDefault());
 }